mirror of
https://github.com/grafana/grafana.git
synced 2025-12-22 04:34:27 +08:00
Compare commits
160 Commits
docs/add-t
...
v7.1.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9893b8c53d | ||
|
|
c702f6cfdb | ||
|
|
b68652547d | ||
|
|
122cd10178 | ||
|
|
4b5c88c66e | ||
|
|
90b80b892d | ||
|
|
da8fc1dd9f | ||
|
|
73926a2988 | ||
|
|
82a235b54c | ||
|
|
5f8d093d3c | ||
|
|
6072444a20 | ||
|
|
af5dd5478e | ||
|
|
7677648978 | ||
|
|
c8b90ec07a | ||
|
|
3016f7b9df | ||
|
|
4e92d2e05b | ||
|
|
5481345ecc | ||
|
|
de837d5b65 | ||
|
|
7d0e7cc1d3 | ||
|
|
327d839283 | ||
|
|
923219640c | ||
|
|
2b84f057a6 | ||
|
|
f15c9981dc | ||
|
|
eb3d77b7bb | ||
|
|
302646e825 | ||
|
|
54f81d2371 | ||
|
|
3a8be3d873 | ||
|
|
c81ed44230 | ||
|
|
17e3d9e407 | ||
|
|
e06f957492 | ||
|
|
5723d951af | ||
|
|
b076394cd3 | ||
|
|
dbb3750610 | ||
|
|
3e4c0bf8f8 | ||
|
|
de815d4746 | ||
|
|
a4b7050609 | ||
|
|
d9868dc2d1 | ||
|
|
7402337bd1 | ||
|
|
5d3b150687 | ||
|
|
d81f6e6925 | ||
|
|
89e62f985d | ||
|
|
94b53ecc08 | ||
|
|
c0fe700c01 | ||
|
|
2f276f97d3 | ||
|
|
f7201c7be2 | ||
|
|
44665ff9fc | ||
|
|
f7d5e4a8c5 | ||
|
|
03b688ce0a | ||
|
|
a89cd8ab7f | ||
|
|
97debfdcce | ||
|
|
6f93a81be0 | ||
|
|
1a545a1826 | ||
|
|
5274070e77 | ||
|
|
4996803e00 | ||
|
|
825c4a5ce0 | ||
|
|
2248c282be | ||
|
|
5e3514749b | ||
|
|
ae3fde6c7f | ||
|
|
3039f9c3bd | ||
|
|
ba5886d42c | ||
|
|
9c7776cd58 | ||
|
|
5b7d1c7fe1 | ||
|
|
e44acff11c | ||
|
|
758c47e13f | ||
|
|
100819fe09 | ||
|
|
cada7a4eb8 | ||
|
|
2d2963561a | ||
|
|
754597e116 | ||
|
|
58a23cd27d | ||
|
|
7031793d06 | ||
|
|
7c0c77cc1c | ||
|
|
3199365ead | ||
|
|
3570bd0cca | ||
|
|
e353e42e96 | ||
|
|
65c46c08d4 | ||
|
|
534feb1e5e | ||
|
|
8101355285 | ||
|
|
226cba97e1 | ||
|
|
7d7f2d9262 | ||
|
|
7ac9057f71 | ||
|
|
8673bd4974 | ||
|
|
f870bea288 | ||
|
|
1ab8109892 | ||
|
|
3655752bae | ||
|
|
d44da8786f | ||
|
|
2df8fe30da | ||
|
|
562e811179 | ||
|
|
2747607805 | ||
|
|
10a9031b1b | ||
|
|
a74d03b807 | ||
|
|
e621f54fe3 | ||
|
|
05a0019fcb | ||
|
|
dfb2119afa | ||
|
|
e931df8ab9 | ||
|
|
1e35109d3e | ||
|
|
ee35db1b8b | ||
|
|
236ac5c394 | ||
|
|
81014a717d | ||
|
|
fab8bea6f7 | ||
|
|
ea469bf48f | ||
|
|
85ca9c933b | ||
|
|
051c7a7c8e | ||
|
|
28978db293 | ||
|
|
56a0b90559 | ||
|
|
461fc94103 | ||
|
|
ee21c1be81 | ||
|
|
a3a9ce7f24 | ||
|
|
1e3265d047 | ||
|
|
88270a2bf8 | ||
|
|
f8bdda7adf | ||
|
|
7618fd36ac | ||
|
|
bca82d8814 | ||
|
|
783e5d12c7 | ||
|
|
e39fef1649 | ||
|
|
2145633e9b | ||
|
|
6503962013 | ||
|
|
40a33c56c2 | ||
|
|
42069bb254 | ||
|
|
4684c7971c | ||
|
|
903eccad20 | ||
|
|
2bc9374a9e | ||
|
|
47e162d6b2 | ||
|
|
b8c5174461 | ||
|
|
eedf6e53de | ||
|
|
5f539230da | ||
|
|
c14ac88b8d | ||
|
|
9417a0c277 | ||
|
|
4c27708b7b | ||
|
|
763d28ad96 | ||
|
|
949988219f | ||
|
|
4aa1d28683 | ||
|
|
a4846ee4fd | ||
|
|
c2403767a0 | ||
|
|
fce35e7aa9 | ||
|
|
0d47601175 | ||
|
|
28e50ae4fd | ||
|
|
fc96444b23 | ||
|
|
9eb16756b1 | ||
|
|
cde6a2de68 | ||
|
|
111c238df2 | ||
|
|
1630f21aee | ||
|
|
c2125493a7 | ||
|
|
33eec937dd | ||
|
|
073f46e289 | ||
|
|
7c1c0bc8c1 | ||
|
|
a9fc5ff45d | ||
|
|
7d1f0d619d | ||
|
|
aa1cdf0a20 | ||
|
|
0b109a1637 | ||
|
|
6a1f05d7ec | ||
|
|
1576b16219 | ||
|
|
44ba5482f1 | ||
|
|
972e07bd2e | ||
|
|
22211e5bdd | ||
|
|
d95c494d9d | ||
|
|
6e3a9d7927 | ||
|
|
4a68ba7b23 | ||
|
|
74ca7121eb | ||
|
|
4d1ea72426 | ||
|
|
11385c6cfe |
@@ -56,7 +56,7 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Install Grafana build pipeline tool"
|
name: "Install Grafana build pipeline tool"
|
||||||
command: |
|
command: |
|
||||||
VERSION=0.4.17
|
VERSION=0.4.19
|
||||||
curl -fLO https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${VERSION}/grabpl
|
curl -fLO https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${VERSION}/grabpl
|
||||||
chmod +x grabpl
|
chmod +x grabpl
|
||||||
mv grabpl /tmp
|
mv grabpl /tmp
|
||||||
|
|||||||
39
.github/ISSUE_TEMPLATE/4-grafana_ui_component.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/4-grafana_ui_component.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: '@grafana/ui component request'
|
||||||
|
about: Suggest a component for the @grafana/ui package
|
||||||
|
labels: 'area/grafana/ui'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
By using this template you will make it easier for us to make sure that documentation and implementation stays up to date for every component in @grafana/ui
|
||||||
|
|
||||||
|
Thank you!
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Why is this component needed**:
|
||||||
|
<!-- Explain your use case -->
|
||||||
|
___
|
||||||
|
- [ ] Is/could it be used in more than one place in Grafana?
|
||||||
|
|
||||||
|
**Where is/could it be used?**:
|
||||||
|
|
||||||
|
___
|
||||||
|
- [ ] Post screenshots possible.
|
||||||
|
- [ ] It has a single use case.
|
||||||
|
- [ ] It is/could be used in multiple places.
|
||||||
|
|
||||||
|
**Implementation** (Checklist meant for the person implementing the component)
|
||||||
|
|
||||||
|
- [ ] Component has a story in Storybook.
|
||||||
|
- [ ] Props and naming follows [our style guide](https://github.com/grafana/grafana/blob/master/contribute/style-guides/frontend.md).
|
||||||
|
- [ ] It is extendable (rest props are spread, styles with className work, and so on).
|
||||||
|
- [ ] Uses [theme for spacing, colors, and so on](https://github.com/grafana/grafana/blob/master/contribute/style-guides/themes.md).
|
||||||
|
- [ ] Works with both light and dark theme.
|
||||||
|
|
||||||
|
**Documentation**
|
||||||
|
|
||||||
|
- [ ] Properties are documented.
|
||||||
|
- [ ] Use cases are described.
|
||||||
|
- [ ] Code examples for the different use cases.
|
||||||
|
- [ ] Dos and don'ts.
|
||||||
|
- [ ] Styling guidelines, specific color usage (if applicable).
|
||||||
@@ -18,6 +18,32 @@ For all items not covered in this guide, refer to the [Microsoft Style Guide](ht
|
|||||||
|
|
||||||
The [codespell](https://github.com/codespell-project/codespell) tool is run for every change to catch common misspellings.
|
The [codespell](https://github.com/codespell-project/codespell) tool is run for every change to catch common misspellings.
|
||||||
|
|
||||||
|
## Inclusive language
|
||||||
|
|
||||||
|
This section provides guidelines on how to avoid using charged language in documentation.
|
||||||
|
|
||||||
|
### Allowing and blocking
|
||||||
|
|
||||||
|
Don't use "whitelist" or "blacklist" when referring to allowing or blocking content or traffic.
|
||||||
|
|
||||||
|
* When used as a noun, use "allowlist" or "blocklist".
|
||||||
|
* When used as a verb, use "allow" or "block"
|
||||||
|
|
||||||
|
Example: _To **allow** outgoing traffic, add the IP to the **allowlist**._
|
||||||
|
|
||||||
|
### Leader and follower
|
||||||
|
|
||||||
|
Don't use "master" or "slave" to describe relationships between nodes or processes.
|
||||||
|
|
||||||
|
* Use "leader", "main" or "primary," instead of "master."
|
||||||
|
* Use "follower" or "secondary," instead of "slave."
|
||||||
|
|
||||||
|
### Exceptions
|
||||||
|
|
||||||
|
When referring to a configuration or settings used by third-party libraries och technologies outside the Grafana project, prefer the original name to avoid confusion.
|
||||||
|
|
||||||
|
For example, use "master" when referring to the default Git branch.
|
||||||
|
|
||||||
## Grafana-specific style
|
## Grafana-specific style
|
||||||
|
|
||||||
The following sections provide general guidelines on topics specific to Grafana documentation. Note that for the most part, these are *guidelines*, not rigid rules. If you have questions, ask in the #docs channel of Grafana Slack.
|
The following sections provide general guidelines on topics specific to Grafana documentation. Note that for the most part, these are *guidelines*, not rigid rules. If you have questions, ask in the #docs channel of Grafana Slack.
|
||||||
@@ -31,7 +57,7 @@ The following sections provide general guidelines on topics specific to Grafana
|
|||||||
* Write in present tense.
|
* Write in present tense.
|
||||||
- Not: The panel will open.
|
- Not: The panel will open.
|
||||||
- Use: The panel opens. Grafana opens the panel.
|
- Use: The panel opens. Grafana opens the panel.
|
||||||
* Do not use an ampersand (&) as an abbreviation for "and."
|
* Do not use an ampersand (&) as an abbreviation for "and."
|
||||||
- **Exceptions:** If an ampersand is used in the Grafana UI, then match the UI.
|
- **Exceptions:** If an ampersand is used in the Grafana UI, then match the UI.
|
||||||
* Avoid using internal slang and jargon in technical documentation.
|
* Avoid using internal slang and jargon in technical documentation.
|
||||||
|
|
||||||
@@ -156,7 +182,7 @@ One word, not two.
|
|||||||
|
|
||||||
#### open source, open-source
|
#### open source, open-source
|
||||||
|
|
||||||
Do not hyphenate when used as an adjective unless the lack of hyphen would cause confusion. For example: _Open source software design is the most open open-source system I can imagine._
|
Do not hyphenate when used as an adjective unless the lack of hyphen would cause confusion. For example: _Open source software design is the most open open-source system I can imagine._
|
||||||
|
|
||||||
Do not hyphenate when it is used as a noun. For example: _Open source is the best way to develop software._
|
Do not hyphenate when it is used as a noun. For example: _Open source is the best way to develop software._
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ The Azure Monitor data source supports multiple services in the Azure cloud:
|
|||||||
- **[Azure Monitor]({{< relref "#querying-the-azure-monitor-service" >}})** is the platform service that provides a single source for monitoring Azure resources.
|
- **[Azure Monitor]({{< relref "#querying-the-azure-monitor-service" >}})** is the platform service that provides a single source for monitoring Azure resources.
|
||||||
- **[Application Insights]({{< relref "#querying-the-application-insights-service" >}})** is an extensible Application Performance Management (APM) service for web developers on multiple platforms and can be used to monitor your live web application - it will automatically detect performance anomalies.
|
- **[Application Insights]({{< relref "#querying-the-application-insights-service" >}})** is an extensible Application Performance Management (APM) service for web developers on multiple platforms and can be used to monitor your live web application - it will automatically detect performance anomalies.
|
||||||
- **[Azure Log Analytics]({{< relref "#querying-the-azure-log-analytics-service" >}})** (or Azure Logs) gives you access to log data collected by Azure Monitor.
|
- **[Azure Log Analytics]({{< relref "#querying-the-azure-log-analytics-service" >}})** (or Azure Logs) gives you access to log data collected by Azure Monitor.
|
||||||
- **[Application Insights Analytics]({{< relref "#writing-analytics-queries-for-the-application-insights-service" >}})** allows you to query [Application Insights data](https://docs.microsoft.com/en-us/azure/azure-monitor/app/analytics) using the same query language used for Azure Log Analytics.
|
- **[Application Insights Analytics]({{< relref "#query-the-application-insights-analytics-service" >}})** allows you to query [Application Insights data](https://docs.microsoft.com/en-us/azure/azure-monitor/app/analytics) using the same query language used for Azure Log Analytics.
|
||||||
|
|
||||||
## Adding the data source
|
## Add the data source
|
||||||
|
|
||||||
The data source can access metrics from four different services. You can configure access to the services that you use. It is also possible to use the same credentials for multiple services if that is how you have set it up in Azure AD.
|
The data source can access metrics from four different services. You can configure access to the services that you use. It is also possible to use the same credentials for multiple services if that is how you have set it up in Azure AD.
|
||||||
|
|
||||||
@@ -76,10 +76,13 @@ In the query editor for a panel, after choosing your Azure Monitor data source,
|
|||||||
- `Azure Monitor`
|
- `Azure Monitor`
|
||||||
- `Application Insights`
|
- `Application Insights`
|
||||||
- `Azure Log Analytics`
|
- `Azure Log Analytics`
|
||||||
|
- `Insights Analytics`
|
||||||
|
|
||||||
The query editor will change depending on which one you pick. Azure Monitor is the default.
|
The query editor changes depending on which one you pick. Azure Monitor is the default.
|
||||||
|
|
||||||
## Querying the Azure Monitor service
|
Starting in Grafana 7.1, Insights Analytics replaced the former edit mode from within Application Insights.
|
||||||
|
|
||||||
|
## Query the Azure Monitor service
|
||||||
|
|
||||||
The Azure Monitor service provides metrics for all the Azure services that you have running. It helps you understand how your applications on Azure are performing and to proactively find issues affecting your applications.
|
The Azure Monitor service provides metrics for all the Azure services that you have running. It helps you understand how your applications on Azure are performing and to proactively find issues affecting your applications.
|
||||||
|
|
||||||
@@ -93,29 +96,34 @@ Examples of metrics that you can get from the service are:
|
|||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/v60/azuremonitor-service-query-editor.png" class="docs-image--no-shadow" caption="Azure Monitor Query Editor" >}}
|
{{< docs-imagebox img="/img/docs/v60/azuremonitor-service-query-editor.png" class="docs-image--no-shadow" caption="Azure Monitor Query Editor" >}}
|
||||||
|
|
||||||
### Formatting legend keys with aliases for Azure Monitor
|
As of Grafana 7.1, the query editor allows you to query multiple dimensions for metrics that support them. Metrics that support multiple dimensions are those listed in the [Azure Monitor supported Metrics List](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-supported) that have one or more values listed in the "Dimension" column for the metric.
|
||||||
|
|
||||||
|
### Format legend keys with aliases for Azure Monitor
|
||||||
|
|
||||||
The default legend formatting for the Azure Monitor API is:
|
The default legend formatting for the Azure Monitor API is:
|
||||||
|
|
||||||
`resourceName{dimensionValue=dimensionName}.metricName`
|
`metricName{dimensionName=dimensionValue,dimensionTwoName=DimensionTwoValue}`
|
||||||
|
|
||||||
These can be quite long but this formatting can be changed using aliases. In the Legend Format field, the aliases which are defined below can be combined any way you want.
|
> **Note:** Before Grafana 7.1, the formatting included the resource name in the default: `resourceName{dimensionName=dimensionValue}.metricName`. As of Grafana 7.1, the resource name has been removed from the default legend.
|
||||||
|
|
||||||
Azure Monitor Examples:
|
These can be quite long, but this formatting can be changed by using aliases. In the **Legend Format** field, you can combine the aliases defined below any way you want.
|
||||||
|
|
||||||
- `dimension: {{dimensionvalue}}`
|
Azure Monitor examples:
|
||||||
- `{{resourcegroup}} - {{resourcename}}`
|
|
||||||
|
- `Blob Type: {{ blobtype }}`
|
||||||
|
- `{{ resourcegroup }} - {{ resourcename }}`
|
||||||
|
|
||||||
### Alias patterns for Azure Monitor
|
### Alias patterns for Azure Monitor
|
||||||
|
|
||||||
- `{{resourcegroup}}` = replaced with the value of the Resource Group
|
- `{{ resourcegroup }}` = replaced with the value of the Resource Group
|
||||||
- `{{namespace}}` = replaced with the value of the Namespace (e.g. Microsoft.Compute/virtualMachines)
|
- `{{ namespace }}` = replaced with the value of the Namespace (e.g. Microsoft.Compute/virtualMachines)
|
||||||
- `{{resourcename}}` = replaced with the value of the Resource Name
|
- `{{ resourcename }}` = replaced with the value of the Resource Name
|
||||||
- `{{metric}}` = replaced with metric name (e.g. Percentage CPU)
|
- `{{ metric }}` = replaced with metric name (e.g. Percentage CPU)
|
||||||
- `{{dimensionname}}` = replaced with dimension key/label (e.g. blobtype)
|
- `{{ dimensionname }}` = *Legacy as of 7.1+ (for backwards compatibility)* replaced with the first dimension's key/label (as sorted by the key/label) (e.g. blobtype)
|
||||||
- `{{dimensionvalue}}` = replaced with dimension value (e.g. BlockBlob)
|
- `{{ dimensionvalue }}` = *Legacy as of 7.1+ (for backwards compatibility)* replaced with first dimension's value (as sorted by the key/label) (e.g. BlockBlob)
|
||||||
|
- `{{ arbitraryDim }}` = *Available in 7.1+* replaced with the value of the corresponding dimension. (e.g. `{{ blobtype }}` becomes BlockBlob)
|
||||||
|
|
||||||
### Templating with variables for Azure Monitor
|
### Create template variables for Azure Monitor
|
||||||
|
|
||||||
Instead of hard-coding things like server, application and sensor name in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
|
Instead of hard-coding things like server, application and sensor name in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
|
||||||
|
|
||||||
@@ -159,29 +167,31 @@ Grafana alerting is supported for the Azure Monitor service. This is not Azure A
|
|||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/v60/azuremonitor-alerting.png" class="docs-image--no-shadow" caption="Azure Monitor Alerting" >}}
|
{{< docs-imagebox img="/img/docs/v60/azuremonitor-alerting.png" class="docs-image--no-shadow" caption="Azure Monitor Alerting" >}}
|
||||||
|
|
||||||
## Querying the Application Insights Service
|
## Query the Application Insights Service
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/v60/appinsights-service-query-editor.png" class="docs-image--no-shadow" caption="Application Insights Query Editor" >}}
|
{{< docs-imagebox img="/img/docs/azuremonitor/insights_metrics_multi-dim.png" class="docs-image--no-shadow" caption="Application Insights Query Editor" >}}
|
||||||
|
|
||||||
|
As of Grafana 7.1, you can select more than one group by dimension.
|
||||||
|
|
||||||
### Formatting legend keys with aliases for Application Insights
|
### Formatting legend keys with aliases for Application Insights
|
||||||
|
|
||||||
The default legend formatting is:
|
The default legend formatting is:
|
||||||
|
|
||||||
`metric/name{group/by="groupbyvalue"}`
|
`metricName{dimensionName=dimensionValue,dimensionTwoName=DimensionTwoValue}`
|
||||||
|
|
||||||
In the Legend Format field, the aliases which are defined below can be combined any way you want.
|
In the Legend Format field, the aliases which are defined below can be combined any way you want.
|
||||||
|
|
||||||
Application Insights Examples:
|
Application Insights examples:
|
||||||
|
|
||||||
- `server: {{groupbyvalue}}`
|
- `city: {{ client/city }}`
|
||||||
- `city: {{groupbyvalue}}`
|
- `{{ metric }} [Location: {{ client/countryOrRegion }}, {{ client/city }}]`
|
||||||
- `{{groupbyname}}: {{groupbyvalue}}`
|
|
||||||
|
|
||||||
### Alias patterns for Application Insights
|
### Alias patterns for Application Insights
|
||||||
|
|
||||||
- `{{groupbyvalue}}` = replaced with the value of the group by
|
- `{{ groupbyvalue }}` = *Legacy as of 7.1+ (for backwards compatibility)* replaced with the first dimension's key/label (as sorted by the key/label)
|
||||||
- `{{groupbyname}}` = replaced with the name/label of the group by
|
- `{{ groupbyname }}` = *Legacy as of 7.1+ (for backwards compatibility)* replaced with first dimension's value (as sorted by the key/label) (e.g. BlockBlob)
|
||||||
- `{{metric}}` = replaced with metric name (e.g. requests/count)
|
- `{{ metric }}` = replaced with metric name (e.g. requests/count)
|
||||||
|
- `{{ arbitraryDim }}` = *Available in 7.1+* replaced with the value of the corresponding dimension. (e.g. `{{ client/city }}` becomes Chicago)
|
||||||
|
|
||||||
### Filter expressions for Application Insights
|
### Filter expressions for Application Insights
|
||||||
|
|
||||||
@@ -222,30 +232,55 @@ Grafana alerting is supported for Application Insights. This is not Azure Alerts
|
|||||||
|
|
||||||
## Querying the Azure Log Analytics service
|
## Querying the Azure Log Analytics service
|
||||||
|
|
||||||
Queries are written in the new [Azure Log Analytics (or KustoDB) Query Language](https://docs.loganalytics.io/index). A Log Analytics Query can be formatted as Time Series data or as Table data.
|
Queries are written in the new [Azure Log Analytics (or KustoDB) Query Language](https://docs.loganalytics.io/index). A Log Analytics query can be formatted as time series data or as table data.
|
||||||
|
|
||||||
Time Series queries are for the Graph Panel (and other panels like the Single Stat panel) and must contain a datetime column, a metric name column and a value column. Here is an example query that returns the aggregated count grouped by the Category column and grouped by hour:
|
If your credentials give you access to multiple subscriptions, then choose the appropriate subscription before entering queries.
|
||||||
|
|
||||||
```
|
### Time series queries
|
||||||
AzureActivity
|
|
||||||
|
Time series queries are for the Graph panel and other panels like the SingleStat panel. Each query must contain at least a datetime column and a numeric value column. The result must also be sorted in ascending order by the datetime column.
|
||||||
|
|
||||||
|
Here is an example query that returns the aggregated count grouped by hour:
|
||||||
|
|
||||||
|
```kusto
|
||||||
|
Perf
|
||||||
| where $__timeFilter(TimeGenerated)
|
| where $__timeFilter(TimeGenerated)
|
||||||
| summarize count() by Category, bin(TimeGenerated, 1h)
|
| summarize count() by bin(TimeGenerated, 1h)
|
||||||
| order by TimeGenerated asc
|
| order by TimeGenerated asc
|
||||||
```
|
```
|
||||||
|
|
||||||
Table queries are mainly used in the Table panel and row a list of columns and rows. This example query returns rows with the 6 specified columns:
|
A query can also have one or more non-numeric/non-datetime columns, and those columns are considered dimensions and become labels in the response. For example, a query that returns the aggregated count grouped by hour, Computer, and the CounterName:
|
||||||
|
|
||||||
|
```kusto
|
||||||
|
Perf
|
||||||
|
| where $__timeFilter(TimeGenerated)
|
||||||
|
| summarize count() by bin(TimeGenerated, 1h), Computer, CounterName
|
||||||
|
| order by TimeGenerated asc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also select additional number value columns (with, or without multiple dimensions). For example, getting a count and average value by hour, Computer, CounterName, and InstanceName:
|
||||||
|
|
||||||
|
```kusto
|
||||||
|
Perf
|
||||||
|
| where $__timeFilter(TimeGenerated)
|
||||||
|
| summarize Samples=count(), AvgValue=avg(CounterValue)
|
||||||
|
by bin(TimeGenerated, $__interval), Computer, CounterName, InstanceName
|
||||||
|
| order by TimeGenerated asc
|
||||||
|
```
|
||||||
|
|
||||||
|
{{< docs-imagebox img="/img/docs/azuremonitor/logs_multi-value_multi-dim.png" class="docs-image--no-shadow" caption="Azure Logs query with multiple values and multiple dimensions" >}}
|
||||||
|
|
||||||
|
### Table queries
|
||||||
|
|
||||||
|
Table queries are mainly used in the Table panel and show a list of columns and rows. This example query returns rows with the six specified columns:
|
||||||
|
|
||||||
|
```kusto
|
||||||
AzureActivity
|
AzureActivity
|
||||||
| where $__timeFilter()
|
| where $__timeFilter()
|
||||||
| project TimeGenerated, ResourceGroup, Category, OperationName, ActivityStatus, Caller
|
| project TimeGenerated, ResourceGroup, Category, OperationName, ActivityStatus, Caller
|
||||||
| order by TimeGenerated desc
|
| order by TimeGenerated desc
|
||||||
```
|
```
|
||||||
|
|
||||||
If your credentials give you access to multiple subscriptions then choose the appropriate subscription first.
|
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/v60/azureloganalytics-service-query-editor.png" class="docs-image--no-shadow" caption="Azure Log Analytics Query Editor" >}}
|
|
||||||
|
|
||||||
### Azure Log Analytics macros
|
### Azure Log Analytics macros
|
||||||
|
|
||||||
To make writing queries easier there are several Grafana macros that can be used in the where clause of a query:
|
To make writing queries easier there are several Grafana macros that can be used in the where clause of a query:
|
||||||
@@ -304,7 +339,7 @@ Example variable queries:
|
|||||||
|
|
||||||
Example of a time series query using variables:
|
Example of a time series query using variables:
|
||||||
|
|
||||||
```
|
```kusto
|
||||||
Perf
|
Perf
|
||||||
| where ObjectName == "$object" and CounterName == "$metric"
|
| where ObjectName == "$object" and CounterName == "$metric"
|
||||||
| where TimeGenerated >= $__timeFrom() and TimeGenerated <= $__timeTo()
|
| where TimeGenerated >= $__timeFrom() and TimeGenerated <= $__timeTo()
|
||||||
@@ -331,21 +366,11 @@ If you're not currently logged in to the Azure Portal, then the link opens the l
|
|||||||
|
|
||||||
Grafana alerting is supported for Application Insights. This is not Azure Alerts support. Read more about how alerting in Grafana works in [Alerting rules]({{< relref "../../alerting/alerts-overview.md" >}}).
|
Grafana alerting is supported for Application Insights. This is not Azure Alerts support. Read more about how alerting in Grafana works in [Alerting rules]({{< relref "../../alerting/alerts-overview.md" >}}).
|
||||||
|
|
||||||
### Writing analytics queries For the Application Insights service
|
## Query the Application Insights Analytics service
|
||||||
|
|
||||||
If you change the service type to "Application Insights", the menu icon to the right adds another option, "Toggle Edit Mode". Once clicked, the query edit mode changes to give you a full text area in which to write log analytics queries. (This is identical to how the InfluxDB data source lets you write raw queries.)
|
If you change the service type to **Insights Analytics**, then a similar editor to the Log Analytics service is available. This service also uses the Kusto language, so the instructions for querying data are identical to [querying the log analytics service]({{< relref "#querying-the-azure-log-analytics-service" >}}), except that you query Application Insights Analytics data instead.
|
||||||
|
|
||||||
Once a query is written, the column names are automatically parsed out of the response data. You can then select them in the "X-axis", "Y-axis", and "Split On" dropdown menus, or just type them out.
|
{{< docs-imagebox img="/img/docs/azuremonitor/insights_analytics_multi-dim.png" class="docs-image--no-shadow" caption="Azure Application Insights Analytics query with multiple dimensions" >}}
|
||||||
|
|
||||||
There are some important caveats to remember:
|
|
||||||
|
|
||||||
- You'll want to order your y-axis in the query, eg. `order by timestamp asc`. The graph may come out looking bizarre otherwise. It's better to have Microsoft sort it on their side where it's faster, than to implement this in the plugin.
|
|
||||||
|
|
||||||
- If you copy a log analytics query, typically they'll end with a render instruction, like `render barchart`. This is unnecessary, but harmless.
|
|
||||||
|
|
||||||
- Currently, four default dashboard variables are supported: `$__timeFilter()`, `$__from`, `$__to`, and `$__interval`. If you're searching in timestamped data, replace the beginning of your where clause to `where $__timeFilter()`. Dashboard changes by time region are handled as you'd expect, as long as you leave the name of the `timestamp` column alone. Likewise, `$__interval` will automatically change based on the dashboard's time region _and_ the width of the chart being displayed. Use it in bins, so `bin(timestamp,$__interval)` changes into something like `bin(timestamp,1s)`. Use `$__from` and `$__to` if you just want the formatted dates to be inserted.
|
|
||||||
|
|
||||||
- Templated dashboard variables are not yet supported! They will come in a future version.
|
|
||||||
|
|
||||||
## Configure the data source with provisioning
|
## Configure the data source with provisioning
|
||||||
|
|
||||||
|
|||||||
@@ -140,10 +140,10 @@ You can use a variable in a metric node path or as a parameter to a function.
|
|||||||
There are two syntaxes:
|
There are two syntaxes:
|
||||||
|
|
||||||
- `$<varname>` Example: apps.frontend.$server.requests.count
|
- `$<varname>` Example: apps.frontend.$server.requests.count
|
||||||
- `[[varname]]` Example: apps.frontend.[[server]].requests.count
|
- `${varname}` Example: apps.frontend.${server}.requests.count
|
||||||
|
|
||||||
Why two ways? The first syntax is easier to read and write but does not allow you to use a variable in the middle of a word. Use
|
Why two ways? The first syntax is easier to read and write but does not allow you to use a variable in the middle of a word. Use
|
||||||
the second syntax in expressions like `my.server[[serverNumber]].count`.
|
the second syntax in expressions like `my.server${serverNumber}.count`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
[Graphite Templated Dashboard](https://play.grafana.org/dashboard/db/graphite-templated-nested)
|
[Graphite Templated Dashboard](https://play.grafana.org/dashboard/db/graphite-templated-nested)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Using InfluxDB in Grafana"
|
title = "Using InfluxDB in Grafana"
|
||||||
description = "Guide for using InfluxDB in Grafana"
|
description = "Guide for using InfluxDB in Grafana"
|
||||||
keywords = ["grafana", "influxdb", "guide"]
|
keywords = ["grafana", "influxdb", "guide", "flux"]
|
||||||
type = "docs"
|
type = "docs"
|
||||||
aliases = ["/docs/grafana/latest/datasources/influxdb"]
|
aliases = ["/docs/grafana/latest/datasources/influxdb"]
|
||||||
[menu.docs]
|
[menu.docs]
|
||||||
@@ -20,14 +20,18 @@ Grafana ships with a feature-rich data source plugin for InfluxDB. The plugin in
|
|||||||
2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`.
|
2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`.
|
||||||
3. Click the `+ Add data source` button in the top header.
|
3. Click the `+ Add data source` button in the top header.
|
||||||
4. Select *InfluxDB* from the *Type* dropdown.
|
4. Select *InfluxDB* from the *Type* dropdown.
|
||||||
|
5. Select *InfluxQL* or *Flux* from the `Query Language` list.
|
||||||
|
|
||||||
> NOTE: If you're not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization.
|
> **Note:** If you're not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization.
|
||||||
|
|
||||||
|
|
||||||
|
### InfluxQL (classic InfluxDB query)
|
||||||
|
|
||||||
Name | Description
|
Name | Description
|
||||||
------------ | -------------
|
------------ | -------------
|
||||||
*Name* | The data source name. This is how you refer to the data source in panels and queries.
|
*Name* | The data source name. This is how you refer to the data source in panels and queries.
|
||||||
*Default* | Default data source means that it will be pre-selected for new panels.
|
*Default* | Default data source means that it will be pre-selected for new panels.
|
||||||
*Url* | The HTTP protocol, IP address and port of your InfluxDB API (InfluxDB API port is by default 8086)
|
*URL* | The HTTP protocol, IP address and port of your InfluxDB API (InfluxDB API port is by default 8086)
|
||||||
*Access* | Server (default) = URL needs to be accessible from the Grafana backend/server, Browser = URL needs to be accessible from the browser.
|
*Access* | Server (default) = URL needs to be accessible from the Grafana backend/server, Browser = URL needs to be accessible from the browser.
|
||||||
*Database* | Name of your InfluxDB database
|
*Database* | Name of your InfluxDB database
|
||||||
*User* | Name of your database user
|
*User* | Name of your database user
|
||||||
@@ -117,6 +121,61 @@ You can switch to raw query mode by clicking hamburger icon and then `Switch edi
|
|||||||
You can remove the group by time by clicking on the `time` part and then the `x` icon. You can
|
You can remove the group by time by clicking on the `time` part and then the `x` icon. You can
|
||||||
change the option `Format As` to `Table` if you want to show raw data in the `Table` panel.
|
change the option `Format As` to `Table` if you want to show raw data in the `Table` panel.
|
||||||
|
|
||||||
|
## Flux support
|
||||||
|
|
||||||
|
> Starting in v7.1, Grafana can execute Flux queries.
|
||||||
|
|
||||||
|
The client supports Flux running on InfluxDB 1.8+. See [1.8 compatibility](https://github.com/influxdata/influxdb-client-go/#influxdb-18-api-compatibility) for more information and connection details.
|
||||||
|
|
||||||
|
|
||||||
|
Name | Description
|
||||||
|
------------ | -------------
|
||||||
|
*URL* | The HTTP protocol, IP address and port of your InfluxDB API (InfluxDB 2.0 API port is by default 9999)
|
||||||
|
*Organization* | The [Influx organization](https://v2.docs.influxdata.com/v2.0/organizations/) that will be used for Flux queries. This is also used to for the `v.organization` query macro
|
||||||
|
*Token* | The authentication token used for Flux queries. With Influx 2.0, use the [influx authentication token to function](https://v2.docs.influxdata.com/v2.0/security/tokens/create-token/). For influx 1.8, the token is `username:password`
|
||||||
|
*Default Bucket* | The [Influx bucket](https://v2.docs.influxdata.com/v2.0/organizations/buckets/) that will be used for the `v.defaultBucket` macro in Flux queries
|
||||||
|
|
||||||
|
You can use the [Flux query and scripting language](https://www.influxdata.com/products/flux/). Grafana's Flux query editor is a text editor for raw Flux queries with Macro support.
|
||||||
|
|
||||||
|
|
||||||
|
### Supported macros
|
||||||
|
|
||||||
|
The macros support copying and pasting from [Chronograph](https://www.influxdata.com/time-series-platform/chronograf/).
|
||||||
|
|
||||||
|
Macro example | Description
|
||||||
|
------------ | -------------
|
||||||
|
*`v.timeRangeStart`* | Will be replaced by the start of the currently active time selection. For example, *2020-06-11T13:31:00Z*
|
||||||
|
*`v.timeRangeEnd`* | Will be replaced by the end of the currently active time selection. For example, *2020-06-11T14:31:00Z*
|
||||||
|
*`v.windowPeriod`* | Will be replaced with an interval string compatible with Flux that corresponds to Grafana's calculated interval based on the time range of the active time selection. For example, *5s*
|
||||||
|
*`v.defaultBucket`* | Will be replaced with the data source configuration's "Default Bucket" setting
|
||||||
|
*`v.organization`* | Will be replaced with the data source configuration's "Organization" setting
|
||||||
|
|
||||||
|
For example, the following query will be interpolated as the query that follows it, with interval and time period values changing according to active time selection\):
|
||||||
|
|
||||||
|
Grafana Flux query:
|
||||||
|
|
||||||
|
```flux
|
||||||
|
from(bucket: v.defaultBucket)
|
||||||
|
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "cpu" or r["_measurement"] == "swap")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "usage_system" or r["_field"] == "free")
|
||||||
|
|> aggregateWindow(every: v.windowPeriod, fn: mean)
|
||||||
|
|> yield(name: "mean")
|
||||||
|
```
|
||||||
|
|
||||||
|
Interpolated query send to Influx:
|
||||||
|
|
||||||
|
```flux
|
||||||
|
from(bucket: "grafana")
|
||||||
|
|> range(start: 2020-06-11T13:59:07Z, stop: 2020-06-11T14:59:07Z)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "cpu" or r["_measurement"] == "swap")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "usage_system" or r["_field"] == "free")
|
||||||
|
|> aggregateWindow(every: 2s, fn: mean)
|
||||||
|
|> yield(name: "mean")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can view the interpolated version of a query with the Query Inspector.
|
||||||
|
|
||||||
## Querying Logs (BETA)
|
## Querying Logs (BETA)
|
||||||
|
|
||||||
> Only available in Grafana v6.3+.
|
> Only available in Grafana v6.3+.
|
||||||
|
|||||||
@@ -47,9 +47,10 @@ You can use this functionality to link to your tracing backend directly from you
|
|||||||
{{< docs-imagebox img="/img/docs/v65/loki_derived_fields.png" class="docs-image--no-shadow" caption="Screenshot of the derived fields configuration" >}}
|
{{< docs-imagebox img="/img/docs/v65/loki_derived_fields.png" class="docs-image--no-shadow" caption="Screenshot of the derived fields configuration" >}}
|
||||||
Each derived field consists of:
|
Each derived field consists of:
|
||||||
|
|
||||||
- **Name:** Shown in the log details as a label.
|
- **Name -** Shown in the log details as a label.
|
||||||
- **Regex:** A Regex pattern that runs on the log message and captures part of it as the value of the new field. Can only contain a single capture group.
|
- **Regex -** A Regex pattern that runs on the log message and captures part of it as the value of the new field. Can only contain a single capture group.
|
||||||
- **URL**: A URL template used to construct a link next to the field value in log details. Use special `${__value.raw}` value in your template to interpolate the real field value into your URL template.
|
- **URL/query -** If the link is external, then enter the full link URL. If the link is internal link, then this input serves as query for the target data source. In both cases, you can interpolate the value from the field with `${__value.raw }` macro.
|
||||||
|
- **Internal link -** Select if the link is internal or external. In case of internal link, a data source selector allows you to select the target data source. Only tracing data sources are supported.
|
||||||
|
|
||||||
You can use a debug section to see what your fields extract and how the URL is interpolated. Click **Show example log message** to show the text area where you can enter a log message.
|
You can use a debug section to see what your fields extract and how the URL is interpolated. Click **Show example log message** to show the text area where you can enter a log message.
|
||||||
{{< docs-imagebox img="/img/docs/v65/loki_derived_fields_debug.png" class="docs-image--no-shadow" caption="Screenshot of the derived fields debugging" >}}
|
{{< docs-imagebox img="/img/docs/v65/loki_derived_fields_debug.png" class="docs-image--no-shadow" caption="Screenshot of the derived fields debugging" >}}
|
||||||
@@ -59,7 +60,7 @@ The new field with the link shown in log details:
|
|||||||
|
|
||||||
## Querying Logs
|
## Querying Logs
|
||||||
|
|
||||||
Querying and displaying log data from Loki is available via [Explore]({{< relref "../explore" >}}), and with the [logs panel]({{< relref "../../panels/visualizations/logs-panel.md" >}}) in dashboards. Select the Loki data source, and then enter a log query to display your logs.
|
Querying and displaying log data from Loki is available via [Explore]({{< relref "../explore" >}}), and with the [logs panel]({{< relref "../../panels/visualizations/logs-panel.md" >}}) in dashboards. Select the Loki data source, and then enter a [LogQL](https://github.com/grafana/loki/blob/master/docs/logql.md) query to display your logs.
|
||||||
|
|
||||||
### Log Queries
|
### Log Queries
|
||||||
|
|
||||||
@@ -193,6 +194,7 @@ datasources:
|
|||||||
derivedFields:
|
derivedFields:
|
||||||
# Field with internal link pointing to data source in Grafana.
|
# Field with internal link pointing to data source in Grafana.
|
||||||
# Right now, Grafana supports only Jaeger and Zipkin data sources as link targets.
|
# Right now, Grafana supports only Jaeger and Zipkin data sources as link targets.
|
||||||
|
# datasourceUid value can be anything, but it should be unique across all defined data source uids.
|
||||||
- datasourceUid: my_jaeger_uid
|
- datasourceUid: my_jaeger_uid
|
||||||
matcherRegex: "traceID=(\\w+)"
|
matcherRegex: "traceID=(\\w+)"
|
||||||
name: TraceID
|
name: TraceID
|
||||||
|
|||||||
101
docs/sources/guides/whats-new-in-v7-1.md
Normal file
101
docs/sources/guides/whats-new-in-v7-1.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
+++
|
||||||
|
title = "What's New in Grafana v7.1"
|
||||||
|
description = "Feature and improvement highlights for Grafana v7.1"
|
||||||
|
keywords = ["grafana", "new", "documentation", "7.1", "release notes"]
|
||||||
|
type = "docs"
|
||||||
|
[menu.docs]
|
||||||
|
name = "Version 7.1"
|
||||||
|
identifier = "v7.1"
|
||||||
|
parent = "whatsnew"
|
||||||
|
weight = -16
|
||||||
|
+++
|
||||||
|
|
||||||
|
# What's new in Grafana v7.1
|
||||||
|
|
||||||
|
This topic includes the release notes for the Grafana v7.1, which is currently in beta. For all details, read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
|
The main highlights are:
|
||||||
|
|
||||||
|
- [**Query history search**]({{< relref "#query-history-search" >}})
|
||||||
|
- [**Provisioning of apps**]({{< relref "#provisioning-of-apps" >}})
|
||||||
|
- [**Azure Monitor Datasource**]({{< relref "#azure-monitor-datasource" >}})
|
||||||
|
- [**Influx Datasource**]({{< relref "#influx-datasource" >}})
|
||||||
|
- [**Deep linking for Google Cloud Monitoring (formerly named Google Stackdriver) datasource**]({{< relref "#deep-linking-for-google-cloud-monitoring-formerly-named-google-stackdriver-datasource" >}})
|
||||||
|
- [**Transforms**]({{< relref "#transforms" >}})
|
||||||
|
- [**Stat panel text mode**]({{< relref "#stat-panel-text-mode" >}})
|
||||||
|
- [**Unification of Explore modes**]({{< relref "#explore-modes-unified" >}})
|
||||||
|
- [**Grafana Enterprise features**]({{< relref "#grafana-enterprise-features" >}})
|
||||||
|
- [**Support for HashiCorp Vault**]({{< relref "#support-for-hashicorp-vault" >}})
|
||||||
|
- [**Internal links for Elastic**]({{< relref "#internal-links-for-elastic" >}})
|
||||||
|
|
||||||
|
## Query history search
|
||||||
|
|
||||||
|
In Grafana v 7.1 we are introducing search functionality in Query history. You can search across queries and your comments. It is especially useful in combination with a time filter and data source filter. Read more about Query history [here]({{<relref "../features/explore/index.md#query-history" >}}).
|
||||||
|
|
||||||
|
{{< docs-imagebox img="/img/docs/v71/query_history_search.gif" max-width="800px" caption="Query history search" >}}
|
||||||
|
|
||||||
|
## Provisioning of apps
|
||||||
|
|
||||||
|
Grafana v7.1 adds support for provisioning of app plugins. This allows app plugins to be configured and enabled/disabled using configuration files. Read more about provisioning of app plugins [here]({{ < relref "../administration/provisioning.md#plugins" >}}).
|
||||||
|
|
||||||
|
## Azure Monitor Datasource
|
||||||
|
|
||||||
|
Support for multiple dimensions has been added to all services in the Azure Monitor datasource. This means you can now group by more than one dimension with time series queries. With the Kusto based services, Log Analytics and Application Insights Analytics, you can also select multiple metrics as well as multiple dimensions.
|
||||||
|
|
||||||
|
Additionally, the “Raw Edit” mode for Application Insights Analytics has been replaced with a new service in the drop down for the datasource and is called “Insights Analytics”. The new query editor behaves in the same way as Log Analytics.
|
||||||
|
|
||||||
|
## Influx Datasource
|
||||||
|
|
||||||
|
Support for Flux and Influx v2 has been added.
|
||||||
|
|
||||||
|
## Deep linking for Google Cloud Monitoring (formerly named Google Stackdriver) datasource
|
||||||
|
|
||||||
|
A new feature in Grafana 7.1 is [deep linking from Grafana panels to the Metrics Explorer in Gooogle Cloud Console]({{<relref "../features/datasources/cloudmonitoring.md#deep-linking-from-grafana-panels-to-the-metrics-explorer-in-google-cloud-console">}}). Click on a time series in the panel to see a context menu with a link to View in Metrics explorer in Google Cloud Console. Clicking that link opens the Metrics explorer in the Monitoring Google Cloud Console and runs the query from the Grafana panel there.
|
||||||
|
|
||||||
|
## Internal links for Elastic
|
||||||
|
|
||||||
|
You can now create links in Elastic configuration that point to another datasource similar to existing feature in
|
||||||
|
Loki. This allows you to link traceID from your logs to tracing data source in Grafana.
|
||||||
|
|
||||||
|
## Transformations
|
||||||
|
|
||||||
|
We have added a new **Merge on time** transform that can combine many time series or table results. Unlike the join transform this combines the result into one table even when the time values does not align / match.
|
||||||
|
|
||||||
|
## Stat panel text mode
|
||||||
|
|
||||||
|
The [stat panel]({{<relref "../panels/visualizations/stat-panel.md#text-mode" >}}) has a new **Text mode** option to control what text to show.
|
||||||
|
|
||||||
|
By default, the Stat panel displays:
|
||||||
|
|
||||||
|
- Just the value for a single series or field.
|
||||||
|
- Both the value and name for multiple series or fields.
|
||||||
|
|
||||||
|
You can use the Text mode option to control what text the panel renders. If the value is not important, only name and color is, then change the `Text mode` to **Name**. The value will still be used to determine color and is displayed in a tooltip.
|
||||||
|
|
||||||
|
{{< docs-imagebox img="/img/docs/v71/stat-panel-text-modes.png" max-width="1025px" caption="Stat panel" >}}
|
||||||
|
|
||||||
|
## Explore modes unified
|
||||||
|
|
||||||
|
Grafana 7.1 includes a major change to Explore: it removes the query mode selector.
|
||||||
|
|
||||||
|
Many data sources tell Grafana whether a response contains time series data or logs data. Using this information, Explore chooses which visualization to use for that data. This means that you don't need to switch back and forth between Logs and Metrics modes depending on the type of query that you want to make.
|
||||||
|
|
||||||
|
## Grafana Enterprise features
|
||||||
|
|
||||||
|
General features are included in the Grafana Enterprise edition software.
|
||||||
|
|
||||||
|
### Support for HashiCorp Vault
|
||||||
|
|
||||||
|
You can now use HashiCorp Vault to get secrets for configuration and provisioning of Grafana Enterprise. Learn more about this feature [here]({{<relref "../enterprise/vault.md">}}).
|
||||||
|
|
||||||
|
### Support for monthly in reports
|
||||||
|
|
||||||
|
With Grafana Enterprise 7.1 you can configure reports to be generated on a [monthly schedule]({{<relref "../enterprise/reporting.md#scheduling">}}).
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
See [upgrade notes]({{<relref "../installation/upgrading.md">}}).
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
Check out [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md) for a complete list of new features, changes, and bug fixes.
|
||||||
@@ -76,16 +76,30 @@ Field overrides allow you to change the settings for one field (column in tables
|
|||||||
1. Navigate to the panel you want to edit, click the panel title, and then click **Edit**.
|
1. Navigate to the panel you want to edit, click the panel title, and then click **Edit**.
|
||||||
1. Click the **Overrides** tab.
|
1. Click the **Overrides** tab.
|
||||||
1. Click **Add override**.
|
1. Click **Add override**.
|
||||||
1. Select a filter option to choose which fields the override applies to.
|
1. Select a [filter option](#filter-options) to choose which fields the override applies to.
|
||||||
|
|
||||||
**Note:** Currently you can only match by name, so after you choose the filter, select which field it applies to from the dropdown list.
|
|
||||||
|
|
||||||
1. Click **Add override property**.
|
1. Click **Add override property**.
|
||||||
1. Select the [field option](#field-options) you want to apply.
|
1. Select the [field option](#field-options) you want to apply.
|
||||||
1. Enter options by adding values in the fields. To return options to default values, delete the white text in the fields.
|
1. Enter options by adding values in the fields. To return options to default values, delete the white text in the fields.
|
||||||
1. Continue to add overrides to this field by clicking **Add override property**, or you can click **Add override** and select a different field to add overrides to.
|
1. Continue to add overrides to this field by clicking **Add override property**, or you can click **Add override** and select a different field to add overrides to.
|
||||||
1. When finished, click **Save** to save all panel edits to the dashboard.
|
1. When finished, click **Save** to save all panel edits to the dashboard.
|
||||||
|
|
||||||
|
## Filter options
|
||||||
|
|
||||||
|
This section explains all available filter options for field overrides. They are listed in alphabetical order.
|
||||||
|
|
||||||
|
### Filter field by name
|
||||||
|
|
||||||
|
Allows you to select a field from the list of all available fields that the override will be applied to.
|
||||||
|
|
||||||
|
### Filter field by name using regex
|
||||||
|
|
||||||
|
Allows you to type in a regular expression against which fields to be overridden will be matched.
|
||||||
|
|
||||||
|
### Filter field by type
|
||||||
|
|
||||||
|
Allows you to select fields by their type (string, numeric, etc).
|
||||||
|
|
||||||
|
|
||||||
## Field options
|
## Field options
|
||||||
|
|
||||||
This section explains all available field options. They are listed in alphabetical order.
|
This section explains all available field options. They are listed in alphabetical order.
|
||||||
@@ -139,7 +153,19 @@ For more information and instructions, refer to [Data links]({{< relref "../link
|
|||||||
|
|
||||||
Lets you set the display title of all fields. You can use [variables]({{< relref "../variables/templates-and-variables.md" >}}) in the field title.
|
Lets you set the display title of all fields. You can use [variables]({{< relref "../variables/templates-and-variables.md" >}}) in the field title.
|
||||||
|
|
||||||
When multiple stats are shown, this field controls the title in each stat. By default this is the series name and field name. You can use expressions like ${__series.name} or ${**field.name} to use only series name or field name in title or \${**cell_2} to refer to other fields (2 being field/column with index 2).
|
When multiple stats, fields, or series are shown, this field controls the title in each stat. You can use expressions like `${__field.name}` to use only the series name or the field name in title.
|
||||||
|
|
||||||
|
Given a field with a name of Temp, and labels of {"Loc"="PBI", "Sensor"="3"}
|
||||||
|
|
||||||
|
| Expression syntax | Example | Renders to | Explanation |
|
||||||
|
| ---------------------------- | ---------------------- | --------------------------------- | ----------- |
|
||||||
|
| `${__field.displayName}` | Same as syntax | `Temp {Loc="PBI", Sensor="3"}` | Displays the field name, and labels in `{}` if they are present. If there is only one label key in the response, then for the label portion, Grafana displays the value of the label without the enclosing braces. |
|
||||||
|
| `${__field.name}` | Same as syntax | `Temp` | Displays the name of the field (without labels). |
|
||||||
|
| `${__field.labels}` | Same as syntax | `Loc="PBI", Sensor="3"` | Displays the labels without the name. |
|
||||||
|
| `${__field.labels.X}` | `${__field.labels.Loc}` | `PBI` | Displays the value of the specified label key. |
|
||||||
|
| `${__field.labels.__values}` | Same as Syntax | `PBI, 3` | Displays the values of the labels separated by a comma (without label keys). |
|
||||||
|
|
||||||
|
If the value is an empty string after rendering the expression for a particular field, then the default display method is used.
|
||||||
|
|
||||||
### Max
|
### Max
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ weight = 300
|
|||||||
|
|
||||||
This page explains what transformations in Grafana are and how to use them.
|
This page explains what transformations in Grafana are and how to use them.
|
||||||
|
|
||||||
> **Note:** This documentation refers to a Grafana 7.0 beta feature. This documentation will be frequently updated to reflect updates to the feature, and it will probably be broken into smaller sections when the feature moves out of beta.
|
> **Note:** This documentation refers to a Grafana 7.0 feature. This documentation will be frequently updated to reflect updates to the feature, and it will probably be broken into smaller sections when the feature moves out of beta.
|
||||||
|
|
||||||
Transformations process the result set before it’s passed to the visualization. You access transformations in the Transform tab of the Grafana panel editor.
|
Transformations process the result set before it’s passed to the visualization. You access transformations in the Transform tab of the Grafana panel editor.
|
||||||
|
|
||||||
@@ -74,6 +74,7 @@ Grafana comes with the following transformations:
|
|||||||
- [Join by field (outer join)](#join-by-field-outer-join)
|
- [Join by field (outer join)](#join-by-field-outer-join)
|
||||||
- [Add field from calculation](#add-field-from-calculation)
|
- [Add field from calculation](#add-field-from-calculation)
|
||||||
- [Labels to fields](#labels-to-fields)
|
- [Labels to fields](#labels-to-fields)
|
||||||
|
- [Series to rows](#series-to-rows)
|
||||||
- [Debug transformations](#debug-transformations)
|
- [Debug transformations](#debug-transformations)
|
||||||
|
|
||||||
Keep reading for detailed descriptions of each type of transformation and the options available for each, as well as suggestions on how to use them.
|
Keep reading for detailed descriptions of each type of transformation and the options available for each, as well as suggestions on how to use them.
|
||||||
@@ -96,25 +97,33 @@ After I apply the transformation, there is no time value and each column has bee
|
|||||||
|
|
||||||
### Merge
|
### Merge
|
||||||
|
|
||||||
Use this transformation to combine the result from multiple queries into one single result based on the time field. This is helpful when using the table panel visualization.
|
> **Note:** This documentation refers to a Grafana 7.1 feature.
|
||||||
|
|
||||||
In the example below, we are visualizing multiple queries returning table data before applying the transformation.
|
Use this transformation to combine the result from multiple queries into one single result. This is helpful when using the table panel visualization. Values that can be merged are combined into the same row. Values are mergeable if the shared fields contains the same data.
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/transformations/table-data-before-merge-7-1.png" class="docs-image--no-shadow" max-width= "1100px" >}}
|
In the example below, we have two queries returning table data. It is visualized as two separate tables before applying the transformation.
|
||||||
|
|
||||||
Here is the same example after applying the merge transformation.
|
Query A:
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/transformations/table-data-after-merge-7-1.png" class="docs-image--no-shadow" max-width= "1100px" >}}
|
| Time | Job | Uptime |
|
||||||
|
|---------------------|---------|-----------|
|
||||||
|
| 2020-07-07 11:34:20 | node | 25260122 |
|
||||||
|
| 2020-07-07 11:24:20 | postgre | 123001233 |
|
||||||
|
|
||||||
If any of the queries return time series data, then a `Metric` column containing the name of the query is added. You can be customized this value by defining `Label` on the source query.
|
Query B:
|
||||||
|
|
||||||
In the example below, we are visualizing multiple queries returning time series data before applying the transformation.
|
| Time | Job | Errors |
|
||||||
|
|---------------------|---------|--------|
|
||||||
|
| 2020-07-07 11:34:20 | node | 15 |
|
||||||
|
| 2020-07-07 11:24:20 | postgre | 5 |
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/transformations/time-series-before-merge-7-1.png" class="docs-image--no-shadow" max-width= "1100px" >}}
|
Here is the result after applying the `Merge` transformation.
|
||||||
|
|
||||||
Here is the same example after applying the merge transformation.
|
| Time | Job | Errors | Uptime |
|
||||||
|
|---------------------|---------|--------|-----------|
|
||||||
|
| 2020-07-07 11:34:20 | node | 15 | 25260122 |
|
||||||
|
| 2020-07-07 11:24:20 | postgre | 5 | 123001233 |
|
||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/transformations/time-series-after-merge-7-1.png" class="docs-image--no-shadow" max-width= "1100px" >}}
|
|
||||||
|
|
||||||
### Filter by name
|
### Filter by name
|
||||||
|
|
||||||
@@ -213,6 +222,43 @@ After I apply the transformation, my labels appear in the table as fields.
|
|||||||
|
|
||||||
{{< docs-imagebox img="/img/docs/transformations/labels-to-fields-after-7-0.png" class="docs-image--no-shadow" max-width= "1100px" >}}
|
{{< docs-imagebox img="/img/docs/transformations/labels-to-fields-after-7-0.png" class="docs-image--no-shadow" max-width= "1100px" >}}
|
||||||
|
|
||||||
|
## Series to rows
|
||||||
|
|
||||||
|
> **Note:** This documentation refers to a Grafana 7.1 feature.
|
||||||
|
|
||||||
|
Use this transformation to combine the result from multiple time series data queries into one single result. This is helpful when using the table panel visualization.
|
||||||
|
|
||||||
|
The result from this transformation will contain three columns: `Time`, `Metric`, and `Value`. The `Metric` column is added so you easily can see from which query the metric originates from. Customize this value by defining `Label` on the source query.
|
||||||
|
|
||||||
|
In the example below, we have two queries returning time series data. It is visualized as two separate tables before applying the transformation.
|
||||||
|
|
||||||
|
Query A:
|
||||||
|
|
||||||
|
| Time | Temperature |
|
||||||
|
|---------------------|-------------|
|
||||||
|
| 2020-07-07 11:34:20 | 25 |
|
||||||
|
| 2020-07-07 10:31:22 | 22 |
|
||||||
|
| 2020-07-07 09:30:05 | 19 |
|
||||||
|
|
||||||
|
Query B:
|
||||||
|
|
||||||
|
| Time | Humidity |
|
||||||
|
|---------------------|----------|
|
||||||
|
| 2020-07-07 11:34:20 | 24 |
|
||||||
|
| 2020-07-07 10:32:20 | 29 |
|
||||||
|
| 2020-07-07 09:30:57 | 33 |
|
||||||
|
|
||||||
|
Here is the result after applying the `Series to rows` transformation.
|
||||||
|
|
||||||
|
| Time | Metric | Value |
|
||||||
|
|---------------------|-------------|-------|
|
||||||
|
| 2020-07-07 11:34:20 | Temperature | 25 |
|
||||||
|
| 2020-07-07 11:34:20 | Humidity | 22 |
|
||||||
|
| 2020-07-07 10:32:20 | Humidity | 29 |
|
||||||
|
| 2020-07-07 10:31:22 | Temperature | 22 |
|
||||||
|
| 2020-07-07 09:30:57 | Humidity | 33 |
|
||||||
|
| 2020-07-07 09:30:05 | Temperature | 19 |
|
||||||
|
|
||||||
## Debug transformations
|
## Debug transformations
|
||||||
|
|
||||||
To see the input and the output result sets of the transformation, click the bug icon on the right side of the transformation row.
|
To see the input and the output result sets of the transformation, click the bug icon on the right side of the transformation row.
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ Use these settings to refine your visualization.
|
|||||||
- **Points -** Display points for values.
|
- **Points -** Display points for values.
|
||||||
- **Point radius -** Controls how large the points are.
|
- **Point radius -** Controls how large the points are.
|
||||||
|
|
||||||
|
### Stacking and null value
|
||||||
|
|
||||||
|
- **Stack -** Each series is stacked on top of another.
|
||||||
|
- **Percent -** Available when **Stack** is selected. Each series is drawn as a percentage of the total of all series.
|
||||||
|
- **Null value -** How null values are displayed. _This is a very important setting._ See note below.
|
||||||
|
- **connected -** If there is a gap in the series, meaning a null value or values, then the line will skip the gap and connect to the next non-null value.
|
||||||
|
- **null -** (default) If there is a gap in the series, meaning a null value, then the line in the graph will be broken and show the gap.
|
||||||
|
- **null as zero -** If there is a gap in the series, meaning a null value, then it will be displayed as a zero value in the graph panel.
|
||||||
|
|
||||||
|
> **Note:** If you are monitoring a server's CPU load and the load reaches 100%, then the server will lock up and the agent sending statistics will not be able to collect the load statistic. This leads to a gap in the metrics and having the default as _null_ means Grafana will show the gaps and indicate that something is wrong. If this is set to _connected_, then it would be easy to miss this signal.
|
||||||
|
|
||||||
### Hover tooltip
|
### Hover tooltip
|
||||||
|
|
||||||
Use these settings to change the appearance of the tooltip that appears when you hover your cursor over the graph visualization.
|
Use these settings to change the appearance of the tooltip that appears when you hover your cursor over the graph visualization.
|
||||||
@@ -44,17 +55,6 @@ Use these settings to change the appearance of the tooltip that appears when you
|
|||||||
- **Increasing -** The series in the hover tooltip are sorted by value and in increasing order, with the lowest value at the top of the list.
|
- **Increasing -** The series in the hover tooltip are sorted by value and in increasing order, with the lowest value at the top of the list.
|
||||||
- **Decreasing -** The series in the hover tooltip are sorted by value and in decreasing order, with the highest value at the top of the list.
|
- **Decreasing -** The series in the hover tooltip are sorted by value and in decreasing order, with the highest value at the top of the list.
|
||||||
|
|
||||||
### Stacking and null value
|
|
||||||
|
|
||||||
- **Stack -** Each series is stacked on top of another.
|
|
||||||
- **Percent -** Available when **Stack** is selected. Each series is drawn as a percentage of the total of all series.
|
|
||||||
- **Null value -** How null values are displayed. _This is a very important setting._ See note below.
|
|
||||||
- **connected -** If there is a gap in the series, meaning a null value or values, then the line will skip the gap and connect to the next non-null value.
|
|
||||||
- **null -** (default) If there is a gap in the series, meaning a null value, then the line in the graph will be broken and show the gap.
|
|
||||||
- **null as zero -** If there is a gap in the series, meaning a null value, then it will be displayed as a zero value in the graph panel.
|
|
||||||
|
|
||||||
> **Note:** If you are monitoring a server's CPU load and the load reaches 100%, then the server will lock up and the agent sending statistics will not be able to collect the load statistic. This leads to a gap in the metrics and having the default as _null_ means Grafana will show the gaps and indicate that something is wrong. If this is set to _connected_, then it would be easy to miss this signal.
|
|
||||||
|
|
||||||
## Series overrides
|
## Series overrides
|
||||||
|
|
||||||
Series overrides allow a series in a graph panel to be rendered differently from the others. You can customize display options on a per-series bases or by using regex rules. For example, one series can have a thicker line width to make it stand out or be moved to the right Y-axis.
|
Series overrides allow a series in a graph panel to be rendered differently from the others. You can customize display options on a per-series bases or by using regex rules. For example, one series can have a thicker line width to make it stand out or be moved to the right Y-axis.
|
||||||
|
|||||||
@@ -10,23 +10,32 @@ weight = 200
|
|||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
|
|
||||||
Grafana has global built-in variables that can be used in expressions in the query editor. This topic lists them in alphabetical order and defines them.
|
Grafana has global built-in variables that can be used in expressions in the query editor. This topic lists them in alphabetical order and defines them. These variables are useful in queries, dashboard links, panel links, and data links.
|
||||||
|
|
||||||
## $__dashboard
|
## $__dashboard
|
||||||
> Only available in Grafana v6.7+
|
|
||||||
|
|
||||||
This variable is the UID of the current dashboard.
|
- `${__dashboard.name}` is the name of the current dashboard
|
||||||
`${__dashboard.name}` is the name of the current dashboard.
|
- `${__dashboard.uid}` is the UID of the current dashboard (used in url)
|
||||||
|
|
||||||
## $__from and $__to
|
## $__from and $__to
|
||||||
|
|
||||||
> Only available in Grafana v6.0+
|
Grafana has two built in time range variables: `$__from` and `$__to`. They are currently always interpolated as epoch milliseconds by default but you can control date formatting.
|
||||||
|
|
||||||
Grafana has two built in time range variables: `$__from` and `$__to`. They are currently always interpolated as epoch milliseconds.
|
> This special formatting syntax is only available in Grafan a 7.1.2+
|
||||||
|
|
||||||
|
| Syntax | Example result | Description |
|
||||||
|
| ------------------------ | ------------------------ | ----------- |
|
||||||
|
| `${__from}` | 1594671549254 | Unix millisecond epoch |
|
||||||
|
| `${__from:date}` | 2020-07-13T20:19:09.254Z | No args, defaults to ISO 8601/RFC 3339 |
|
||||||
|
| `${__from:date:iso}` | 2020-07-13T20:19:09.254Z | ISO 8601/RFC 3339 |
|
||||||
|
| `${__from:date:seconds}` | 1594671549 | Unix seconds epoch |
|
||||||
|
| `${__from:date:YYYY-MM}` | 2020-07 | Any custom [date format](https://momentjs.com/docs/#/displaying/) |
|
||||||
|
|
||||||
|
The above syntax works with `${__to}` as well.
|
||||||
|
|
||||||
## $__interval
|
## $__interval
|
||||||
|
|
||||||
The `$__interval` variable can be used as a parameter to group by time (for InfluxDB, MySQL, Postgres, MSSQL), Date histogram interval (for Elasticsearch) or as a *summarize* function parameter (for Graphite).
|
You can use the `$__interval` variable as a parameter to group by time (for InfluxDB, MySQL, Postgres, MSSQL), Date histogram interval (for Elasticsearch), or as a _summarize_ function parameter (for Graphite).
|
||||||
|
|
||||||
Grafana automatically calculates an interval that can be used to group by time in queries. When there are more data points than can be shown on a graph then queries can be made more efficient by grouping by a larger interval. It is more efficient to group by 1 day than by 10s when looking at 3 months of data and the graph will look the same and the query will be faster. The `$__interval` is calculated using the time range and the width of the graph (the number of pixels).
|
Grafana automatically calculates an interval that can be used to group by time in queries. When there are more data points than can be shown on a graph then queries can be made more efficient by grouping by a larger interval. It is more efficient to group by 1 day than by 10s when looking at 3 months of data and the graph will look the same and the query will be faster. The `$__interval` is calculated using the time range and the width of the graph (the number of pixels).
|
||||||
|
|
||||||
@@ -47,12 +56,12 @@ This variable is the `$__interval` variable in milliseconds, not a time interval
|
|||||||
This variable is only available in the Singlestat panel and can be used in the prefix or suffix fields on the Options tab. The variable will be replaced with the series name or alias.
|
This variable is only available in the Singlestat panel and can be used in the prefix or suffix fields on the Options tab. The variable will be replaced with the series name or alias.
|
||||||
|
|
||||||
## $__org
|
## $__org
|
||||||
> Only available in Grafana v6.7+
|
|
||||||
|
|
||||||
This variable is the ID of the current organization.
|
This variable is the ID of the current organization.
|
||||||
`${__org.name}` is the name of the current organization.
|
`${__org.name}` is the name of the current organization.
|
||||||
|
|
||||||
## $__user
|
## $__user
|
||||||
|
|
||||||
> Only available in Grafana v7.1+
|
> Only available in Grafana v7.1+
|
||||||
|
|
||||||
`${__user.id}` is the ID of the current user.
|
`${__user.id}` is the ID of the current user.
|
||||||
@@ -60,8 +69,6 @@ This variable is the ID of the current organization.
|
|||||||
|
|
||||||
## $__range
|
## $__range
|
||||||
|
|
||||||
> Only available in Grafana v5.3+
|
|
||||||
|
|
||||||
Currently only supported for Prometheus data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`.
|
Currently only supported for Prometheus data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`.
|
||||||
|
|
||||||
## $timeFilter or $__timeFilter
|
## $timeFilter or $__timeFilter
|
||||||
@@ -69,7 +76,8 @@ Currently only supported for Prometheus data sources. This variable represents t
|
|||||||
The `$timeFilter` variable returns the currently selected time range as an expression. For example, the time range interval `Last 7 days` expression is `time > now() - 7d`.
|
The `$timeFilter` variable returns the currently selected time range as an expression. For example, the time range interval `Last 7 days` expression is `time > now() - 7d`.
|
||||||
|
|
||||||
This is used in several places, including:
|
This is used in several places, including:
|
||||||
* The WHERE clause for the InfluxDB data source. Grafana adds it automatically to InfluxDB queries when in Query Editor Mode. It has to be added manually in Text Editor Mode: `WHERE $timeFilter`.
|
|
||||||
* Log Analytics queries in the Azure Monitor data source.
|
- The WHERE clause for the InfluxDB data source. Grafana adds it automatically to InfluxDB queries when in Query Editor mode. You can add it manually in Text Editor mode: `WHERE $timeFilter`.
|
||||||
* SQL queries in MySQL, Postgres, and MSSQL
|
- Log Analytics queries in the Azure Monitor data source.
|
||||||
* The `$__timeFilter` variable is used in the MySQL data source.
|
- SQL queries in MySQL, Postgres, and MSSQL
|
||||||
|
- The `$__timeFilter` variable is used in the MySQL data source.
|
||||||
|
|||||||
37
go.mod
37
go.mod
@@ -2,7 +2,12 @@ module github.com/grafana/grafana
|
|||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
|
// Override xorm's outdated go-mssqldb dependency, since we can't upgrade to current xorm (due to breaking changes).
|
||||||
|
// We need a more current go-mssqldb so we get rid of a version of apache/thrift with vulnerabilities.
|
||||||
|
replace github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go v0.60.0 // indirect
|
||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
|
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
|
||||||
github.com/aws/aws-sdk-go v1.29.20
|
github.com/aws/aws-sdk-go v1.29.20
|
||||||
@@ -12,7 +17,8 @@ require (
|
|||||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
|
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
|
||||||
github.com/crewjam/saml v0.0.0-20191031171751-c42136edf9b1
|
github.com/crewjam/saml v0.0.0-20191031171751-c42136edf9b1
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4
|
github.com/deepmap/oapi-codegen v1.3.11 // indirect
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec
|
||||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
||||||
github.com/facebookgo/inject v0.0.0-20180706035515-f23751cae28b
|
github.com/facebookgo/inject v0.0.0-20180706035515-f23751cae28b
|
||||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||||
@@ -25,17 +31,17 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/go-stack/stack v1.8.0
|
github.com/go-stack/stack v1.8.0
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/golang/protobuf v1.4.0
|
github.com/golang/protobuf v1.4.2
|
||||||
github.com/google/go-cmp v0.4.0
|
github.com/google/go-cmp v0.5.0
|
||||||
github.com/gorilla/websocket v1.4.1
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/gosimple/slug v1.4.2
|
github.com/gosimple/slug v1.4.2
|
||||||
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
|
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
|
||||||
github.com/grafana/grafana-plugin-sdk-go v0.70.0
|
github.com/grafana/grafana-plugin-sdk-go v0.75.0
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd
|
||||||
github.com/hashicorp/go-plugin v1.2.2
|
github.com/hashicorp/go-plugin v1.2.2
|
||||||
github.com/hashicorp/go-version v1.1.0
|
github.com/hashicorp/go-version v1.1.0
|
||||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
|
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
|
||||||
github.com/influxdata/influxdb-client-go v1.3.0
|
github.com/influxdata/influxdb-client-go/v2 v2.0.1
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
|
||||||
github.com/jung-kurt/gofpdf v1.10.1
|
github.com/jung-kurt/gofpdf v1.10.1
|
||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
|
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
|
||||||
@@ -43,15 +49,15 @@ require (
|
|||||||
github.com/klauspost/cpuid v1.2.0 // indirect
|
github.com/klauspost/cpuid v1.2.0 // indirect
|
||||||
github.com/lib/pq v1.2.0
|
github.com/lib/pq v1.2.0
|
||||||
github.com/linkedin/goavro/v2 v2.9.7
|
github.com/linkedin/goavro/v2 v2.9.7
|
||||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12
|
github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/mattn/go-sqlite3 v1.11.0
|
github.com/mattn/go-sqlite3 v1.11.0
|
||||||
github.com/opentracing/opentracing-go v1.1.0
|
github.com/opentracing/opentracing-go v1.1.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.3.0
|
github.com/prometheus/client_golang v1.6.0
|
||||||
github.com/prometheus/client_model v0.1.0
|
github.com/prometheus/client_model v0.2.0
|
||||||
github.com/prometheus/common v0.7.0
|
github.com/prometheus/common v0.9.1
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
|
||||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
||||||
github.com/robfig/cron/v3 v3.0.0
|
github.com/robfig/cron/v3 v3.0.0
|
||||||
@@ -70,10 +76,13 @@ require (
|
|||||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
|
||||||
github.com/yudai/pp v2.0.1+incompatible // indirect
|
github.com/yudai/pp v2.0.1+incompatible // indirect
|
||||||
go.uber.org/atomic v1.5.1 // indirect
|
go.uber.org/atomic v1.5.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
|
||||||
|
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d // indirect
|
||||||
|
golang.org/x/text v0.3.3 // indirect
|
||||||
|
golang.org/x/tools v0.0.0-20200708183856-df98bc6d456c // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||||
google.golang.org/grpc v1.29.1
|
google.golang.org/grpc v1.29.1
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
@@ -84,7 +93,7 @@ require (
|
|||||||
gopkg.in/mail.v2 v2.3.1
|
gopkg.in/mail.v2 v2.3.1
|
||||||
gopkg.in/redis.v5 v5.2.9
|
gopkg.in/redis.v5 v5.2.9
|
||||||
gopkg.in/square/go-jose.v2 v2.4.1
|
gopkg.in/square/go-jose.v2 v2.4.1
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
xorm.io/core v0.7.3
|
xorm.io/core v0.7.3
|
||||||
xorm.io/xorm v0.8.1
|
xorm.io/xorm v0.8.1
|
||||||
)
|
)
|
||||||
|
|||||||
320
go.sum
320
go.sum
@@ -1,11 +1,37 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.60.0 h1:R+tDlceO7Ss+zyvtsdhTxacDyZ1k99xwskQ4FT7ruoM=
|
||||||
|
cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
|
||||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0=
|
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0=
|
||||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0=
|
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@@ -14,7 +40,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/apache/arrow/go/arrow v0.0.0-20200629181129-68b1273cbbf7 h1:dgL2mSOuj63SXOyojjWKq2ni3FQpQ+KrLKD7Pbq6t/4=
|
github.com/apache/arrow/go/arrow v0.0.0-20200629181129-68b1273cbbf7 h1:dgL2mSOuj63SXOyojjWKq2ni3FQpQ+KrLKD7Pbq6t/4=
|
||||||
github.com/apache/arrow/go/arrow v0.0.0-20200629181129-68b1273cbbf7/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0=
|
github.com/apache/arrow/go/arrow v0.0.0-20200629181129-68b1273cbbf7/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
|
||||||
github.com/aws/aws-sdk-go v1.29.20 h1:vAHJhARpdbdeJstTVaugeHgvVj5lBnfz3blbbD24gfo=
|
github.com/aws/aws-sdk-go v1.29.20 h1:vAHJhARpdbdeJstTVaugeHgvVj5lBnfz3blbbD24gfo=
|
||||||
github.com/aws/aws-sdk-go v1.29.20/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
|
github.com/aws/aws-sdk-go v1.29.20/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
|
||||||
github.com/beevik/etree v1.0.1/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
github.com/beevik/etree v1.0.1/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||||
@@ -34,6 +59,9 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
|
|||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
|
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
|
||||||
@@ -53,13 +81,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||||
github.com/deepmap/oapi-codegen v1.3.6 h1:Wj44p9A0V0PJ+AUg0BWdyGcsS1LY18U+0rCuPQgK0+o=
|
github.com/deepmap/oapi-codegen v1.3.6 h1:Wj44p9A0V0PJ+AUg0BWdyGcsS1LY18U+0rCuPQgK0+o=
|
||||||
github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU=
|
github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
|
github.com/deepmap/oapi-codegen v1.3.11 h1:Nd3tDQfqgquLmCzyRONHzs5SJEwPPoQcFZxT8MKt1Hs=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec h1:NfhRXXFDPxcF5Cwo06DzeIaE7uuJtAUhsDwH3LNsjos=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
@@ -80,8 +107,12 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk=
|
github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk=
|
||||||
|
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
@@ -104,52 +135,72 @@ github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuq
|
|||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A=
|
github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A=
|
||||||
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/gosimple/slug v1.4.2 h1:jDmprx3q/9Lfk4FkGZtvzDQ9Cj9eAmsjzeQGp24PeiQ=
|
github.com/gosimple/slug v1.4.2 h1:jDmprx3q/9Lfk4FkGZtvzDQ9Cj9eAmsjzeQGp24PeiQ=
|
||||||
github.com/gosimple/slug v1.4.2/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
|
github.com/gosimple/slug v1.4.2/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
|
||||||
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 h1:SPdxCL9BChFTlyi0Khv64vdCW4TMna8+sxL7+Chx+Ag=
|
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 h1:SPdxCL9BChFTlyi0Khv64vdCW4TMna8+sxL7+Chx+Ag=
|
||||||
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4/go.mod h1:nc0XxBzjeGcrMltCDw269LoWF9S8ibhgxolCdA1R8To=
|
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4/go.mod h1:nc0XxBzjeGcrMltCDw269LoWF9S8ibhgxolCdA1R8To=
|
||||||
github.com/grafana/grafana-plugin-sdk-go v0.70.0 h1:tbwf0KMp8QEQQYF3bDBOOv/npegD6YP8T90OWbLr7n4=
|
github.com/grafana/grafana-plugin-sdk-go v0.75.0 h1:b0Ugpn88VNx17Q8MA1MsWkD3ddBEbRvFxzid+Nka3F0=
|
||||||
github.com/grafana/grafana-plugin-sdk-go v0.70.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=
|
github.com/grafana/grafana-plugin-sdk-go v0.75.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||||
@@ -162,15 +213,17 @@ github.com/hashicorp/go-plugin v1.2.2/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYt
|
|||||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178=
|
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178=
|
||||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||||
github.com/influxdata/influxdb-client-go v1.3.0 h1:R3hLAFtbXCCsPNjlRY8CD66B2ROYWT3miLYLuTIlbbw=
|
github.com/influxdata/influxdb-client-go/v2 v2.0.1 h1:vRla3taM+zkziP1NUGfN6Y6zJ9ZSSMg0fs/JhCGyX1s=
|
||||||
github.com/influxdata/influxdb-client-go v1.3.0/go.mod h1:S+oZsPivqbcP1S9ur+T+QqXvrYS3NCZeMQtBoH4D1dw=
|
github.com/influxdata/influxdb-client-go/v2 v2.0.1/go.mod h1:eyFPc0lhFnNSpyCDb0ZkrB3Hbtqvn1K1JZmjo2BXqeo=
|
||||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
|
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
|
||||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||||
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||||
@@ -180,9 +233,11 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht
|
|||||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
@@ -216,15 +271,14 @@ github.com/linkedin/goavro/v2 v2.9.7 h1:Vd++Rb/RKcmNJjM0HP/JJFMEWa21eUBVKPYlKehO
|
|||||||
github.com/linkedin/goavro/v2 v2.9.7/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
|
github.com/linkedin/goavro/v2 v2.9.7/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
|
||||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
|
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
|
||||||
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
|
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
|
||||||
github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
|
|
||||||
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||||
github.com/mattetti/filebuffer v1.0.0 h1:ixTvQ0JjBTwWbdpDZ98lLrydo7KRi8xNRIi5RFszsbY=
|
github.com/mattetti/filebuffer v1.0.0 h1:ixTvQ0JjBTwWbdpDZ98lLrydo7KRi8xNRIi5RFszsbY=
|
||||||
github.com/mattetti/filebuffer v1.0.0/go.mod h1:X6nyAIge2JGVmuJt2MFCqmHrb/5IHiphfHtot0s5cnI=
|
github.com/mattetti/filebuffer v1.0.0/go.mod h1:X6nyAIge2JGVmuJt2MFCqmHrb/5IHiphfHtot0s5cnI=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
@@ -261,12 +315,10 @@ github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
|||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -274,32 +326,32 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc=
|
|
||||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||||
|
github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A=
|
||||||
|
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE=
|
|
||||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||||
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
|
|
||||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
|
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||||
|
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
|
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
|
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
|
||||||
|
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
|
||||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE=
|
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE=
|
||||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||||
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
||||||
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
|
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
|
||||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
|
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
@@ -359,10 +411,16 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn
|
|||||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||||
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
|
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
|
||||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/zenazn/goji v0.9.1-0.20160507202103-64eb34159fe5/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.1-0.20160507202103-64eb34159fe5/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
|
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
|
||||||
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
@@ -371,41 +429,83 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||||
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA=
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
|
||||||
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -414,79 +514,180 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d h1:QQrM/CCYEzTs91GZylDCQjGHudbPTxF/1fvXdVh5lMo=
|
||||||
|
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f h1:kDxGY2VmgABOe55qheT/TFqUMtcTHnomIPS1iv3G4Ms=
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200708183856-df98bc6d456c h1:Jt8nybBNSGn80qEV8fQLwCam6RQeX4dsxit8if67Sfc=
|
||||||
|
golang.org/x/tools v0.0.0-20200708183856-df98bc6d456c/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5 h1:a/Sqq5B3dGnmxhuJZIHFsIxhEkqElErr5TaU6IqBAj0=
|
||||||
|
google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
@@ -497,13 +698,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
|
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
|
||||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
|
gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
|
||||||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
|
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
|
||||||
gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs=
|
|
||||||
gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI=
|
gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI=
|
||||||
gopkg.in/macaron.v1 v1.3.9 h1:Dw+DDRYdXgQyEsPlfAfKz+UA5qVUrH3KPD7JhmZ9MFc=
|
gopkg.in/macaron.v1 v1.3.9 h1:Dw+DDRYdXgQyEsPlfAfKz+UA5qVUrH3KPD7JhmZ9MFc=
|
||||||
gopkg.in/macaron.v1 v1.3.9/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
gopkg.in/macaron.v1 v1.3.9/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
||||||
@@ -520,12 +721,17 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"packages": ["packages/*"],
|
"packages": ["packages/*"],
|
||||||
"version": "7.1.0-pre.0"
|
"version": "7.1.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "grafana",
|
"name": "grafana",
|
||||||
"version": "7.1.0-pre",
|
"version": "7.1.5",
|
||||||
"repository": "github:grafana/grafana",
|
"repository": "github:grafana/grafana",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
|
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
|
||||||
@@ -224,6 +224,7 @@
|
|||||||
"core-js": "3.6.4",
|
"core-js": "3.6.4",
|
||||||
"d3": "5.15.0",
|
"d3": "5.15.0",
|
||||||
"d3-scale-chromatic": "1.5.0",
|
"d3-scale-chromatic": "1.5.0",
|
||||||
|
"dangerously-set-html-content": "1.0.6",
|
||||||
"emotion": "10.0.27",
|
"emotion": "10.0.27",
|
||||||
"eventemitter3": "4.0.0",
|
"eventemitter3": "4.0.0",
|
||||||
"fast-text-encoding": "^1.0.0",
|
"fast-text-encoding": "^1.0.0",
|
||||||
@@ -267,7 +268,7 @@
|
|||||||
"regenerator-runtime": "0.13.3",
|
"regenerator-runtime": "0.13.3",
|
||||||
"reselect": "4.0.0",
|
"reselect": "4.0.0",
|
||||||
"rst2html": "github:thoward/rst2html#990cb89",
|
"rst2html": "github:thoward/rst2html#990cb89",
|
||||||
"rxjs": "6.5.5",
|
"rxjs": "6.6.0",
|
||||||
"search-query-parser": "1.5.4",
|
"search-query-parser": "1.5.4",
|
||||||
"slate": "0.47.8",
|
"slate": "0.47.8",
|
||||||
"slate-plain-serializer": "0.7.10",
|
"slate-plain-serializer": "0.7.10",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/data",
|
"name": "@grafana/data",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"description": "Grafana Data Library",
|
"description": "Grafana Data Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"typescript"
|
"typescript"
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"@braintree/sanitize-url": "4.0.0",
|
"@braintree/sanitize-url": "4.0.0",
|
||||||
"apache-arrow": "0.16.0",
|
"apache-arrow": "0.16.0",
|
||||||
"lodash": "4.17.15",
|
"lodash": "4.17.15",
|
||||||
"rxjs": "6.5.5",
|
"rxjs": "6.6.0",
|
||||||
"xss": "1.0.6"
|
"xss": "1.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const buildCjsPackage = ({ env }) => {
|
|||||||
globals: {},
|
globals: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
external: ['lodash', 'apache-arrow'], // Use Lodash & arrow from grafana
|
external: ['lodash', 'rxjs', 'apache-arrow'], // Use Lodash, rxjs & arrow from grafana
|
||||||
plugins: [
|
plugins: [
|
||||||
json({
|
json({
|
||||||
include: ['../../node_modules/moment-timezone/data/packed/latest.json'],
|
include: ['../../node_modules/moment-timezone/data/packed/latest.json'],
|
||||||
|
|||||||
12
packages/grafana-data/src/dataframe/utils.ts
Normal file
12
packages/grafana-data/src/dataframe/utils.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { DataFrame, FieldType } from '../types/dataFrame';
|
||||||
|
|
||||||
|
export const isTimeSerie = (frame: DataFrame): boolean => {
|
||||||
|
if (frame.fields.length > 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!frame.fields.find(field => field.type === FieldType.time);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isTimeSeries = (data: DataFrame[]): boolean => {
|
||||||
|
return !data.find(frame => !isTimeSerie(frame));
|
||||||
|
};
|
||||||
@@ -5,7 +5,7 @@ export interface DateTimeBuiltinFormat {
|
|||||||
__momentBuiltinFormatBrand: any;
|
__momentBuiltinFormatBrand: any;
|
||||||
}
|
}
|
||||||
export const ISO_8601: DateTimeBuiltinFormat = moment.ISO_8601;
|
export const ISO_8601: DateTimeBuiltinFormat = moment.ISO_8601;
|
||||||
export type DateTimeInput = Date | string | number | Array<string | number> | DateTime; // null | undefined
|
export type DateTimeInput = Date | string | number | Array<string | number> | DateTime | null; // | undefined;
|
||||||
export type FormatInput = string | DateTimeBuiltinFormat | undefined;
|
export type FormatInput = string | DateTimeBuiltinFormat | undefined;
|
||||||
export type DurationInput = string | number | DateTimeDuration;
|
export type DurationInput = string | number | DateTimeDuration;
|
||||||
export type DurationUnit =
|
export type DurationUnit =
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ export const isValidTimeSpan = (value: string) => {
|
|||||||
return info.invalid !== true;
|
return info.invalid !== true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const describeTimeRangeAbbrevation = (range: TimeRange, timeZone?: TimeZone) => {
|
export const describeTimeRangeAbbreviation = (range: TimeRange, timeZone?: TimeZone) => {
|
||||||
if (isDateTime(range.from)) {
|
if (isDateTime(range.from)) {
|
||||||
return timeZoneAbbrevation(range.from, { timeZone });
|
return timeZoneAbbrevation(range.from, { timeZone });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ describe('Format value', () => {
|
|||||||
|
|
||||||
it('should return formatted value if there are no matching value mappings', () => {
|
it('should return formatted value if there are no matching value mappings', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 0, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
{ id: 1, operator: '', text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
|
{ id: 1, text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
|
||||||
];
|
];
|
||||||
const value = '10';
|
const value = '10';
|
||||||
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
||||||
@@ -186,8 +186,8 @@ describe('Format value', () => {
|
|||||||
|
|
||||||
it('should return mapped value if there are matching value mappings', () => {
|
it('should return mapped value if there are matching value mappings', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
||||||
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
];
|
];
|
||||||
const value = '11';
|
const value = '11';
|
||||||
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
||||||
@@ -196,9 +196,7 @@ describe('Format value', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return mapped value and leave numeric value in tact if value mapping maps to empty string', () => {
|
it('should return mapped value and leave numeric value in tact if value mapping maps to empty string', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [{ id: 1, text: '', type: MappingType.ValueToText, value: '1' }];
|
||||||
{ id: 1, operator: '', text: '', type: MappingType.ValueToText, value: '1' },
|
|
||||||
];
|
|
||||||
const value = '1';
|
const value = '1';
|
||||||
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export interface ReduceDataOptions {
|
|||||||
|
|
||||||
// TODO: use built in variables, same as for data links?
|
// TODO: use built in variables, same as for data links?
|
||||||
export const VAR_SERIES_NAME = '__series.name';
|
export const VAR_SERIES_NAME = '__series.name';
|
||||||
export const VAR_FIELD_NAME = '__field.name';
|
export const VAR_FIELD_NAME = '__field.displayName'; // Includes the rendered tags and naming strategy
|
||||||
export const VAR_FIELD_LABELS = '__field.labels';
|
export const VAR_FIELD_LABELS = '__field.labels';
|
||||||
export const VAR_CALC = '__calc';
|
export const VAR_CALC = '__calc';
|
||||||
export const VAR_CELL_PREFIX = '__cell_'; // consistent with existing table templates
|
export const VAR_CELL_PREFIX = '__cell_'; // consistent with existing table templates
|
||||||
|
|||||||
@@ -127,11 +127,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
Object {
|
Object {
|
||||||
"__field": Object {
|
"__field": Object {
|
||||||
"text": "Field",
|
"text": "Field",
|
||||||
"value": Object {
|
"value": Object {},
|
||||||
"formattedLabels": "",
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "A message",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"__series": Object {
|
"__series": Object {
|
||||||
"text": "Series",
|
"text": "Series",
|
||||||
@@ -146,11 +142,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
Object {
|
Object {
|
||||||
"__field": Object {
|
"__field": Object {
|
||||||
"text": "Field",
|
"text": "Field",
|
||||||
"value": Object {
|
"value": Object {},
|
||||||
"formattedLabels": "",
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "B info",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"__series": Object {
|
"__series": Object {
|
||||||
"text": "Series",
|
"text": "Series",
|
||||||
@@ -545,7 +537,7 @@ describe('getLinksSupplier', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: 'testDS',
|
title: 'testDS',
|
||||||
href:
|
href:
|
||||||
'/explore?left={"datasource":"testDS","queries":["12345"],"mode":"Metrics","ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}',
|
'/explore?left={"datasource":"testDS","queries":["12345"],"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}',
|
||||||
onClick: undefined,
|
onClick: undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
|
|||||||
import { DataLinkBuiltInVars, locationUtil } from '../utils';
|
import { DataLinkBuiltInVars, locationUtil } from '../utils';
|
||||||
import { formattedValueToString } from '../valueFormats';
|
import { formattedValueToString } from '../valueFormats';
|
||||||
import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
|
import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
|
||||||
import { formatLabels } from '../utils/labels';
|
|
||||||
import { getFrameDisplayName, getFieldDisplayName } from './fieldState';
|
import { getFrameDisplayName, getFieldDisplayName } from './fieldState';
|
||||||
import { getTimeField } from '../dataframe/processDataFrame';
|
import { getTimeField } from '../dataframe/processDataFrame';
|
||||||
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
||||||
|
import { getTemplateProxyForField } from './templateProxies';
|
||||||
|
|
||||||
interface OverrideProps {
|
interface OverrideProps {
|
||||||
match: FieldMatcher;
|
match: FieldMatcher;
|
||||||
@@ -113,11 +113,7 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
|
|||||||
|
|
||||||
fieldScopedVars['__field'] = {
|
fieldScopedVars['__field'] = {
|
||||||
text: 'Field',
|
text: 'Field',
|
||||||
value: {
|
value: getTemplateProxyForField(field, frame, options.data),
|
||||||
name: displayName, // Generally appropriate (may include the series name if useful)
|
|
||||||
formattedLabels: formatLabels(field.labels!),
|
|
||||||
labels: field.labels,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
field.state = {
|
field.state = {
|
||||||
|
|||||||
32
packages/grafana-data/src/field/templateProxies.test.ts
Normal file
32
packages/grafana-data/src/field/templateProxies.test.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { getTemplateProxyForField } from './templateProxies';
|
||||||
|
import { toDataFrame } from '../dataframe';
|
||||||
|
|
||||||
|
describe('Template proxies', () => {
|
||||||
|
it('supports name and displayName', () => {
|
||||||
|
const frames = [
|
||||||
|
toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: '🔥',
|
||||||
|
config: { displayName: '✨' },
|
||||||
|
labels: {
|
||||||
|
b: 'BBB',
|
||||||
|
a: 'AAA',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const f = getTemplateProxyForField(frames[0].fields[0], frames[0], frames);
|
||||||
|
|
||||||
|
expect(f.name).toEqual('🔥');
|
||||||
|
expect(f.displayName).toEqual('✨');
|
||||||
|
expect(`${f.labels}`).toEqual('a="AAA", b="BBB"');
|
||||||
|
expect(f.labels.__values).toEqual('AAA, BBB');
|
||||||
|
expect(f.labels.a).toEqual('AAA');
|
||||||
|
|
||||||
|
// Deprecated syntax
|
||||||
|
expect(`${f.formattedLabels}`).toEqual('a="AAA", b="BBB"');
|
||||||
|
});
|
||||||
|
});
|
||||||
41
packages/grafana-data/src/field/templateProxies.ts
Normal file
41
packages/grafana-data/src/field/templateProxies.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { DataFrame, Field } from '../types';
|
||||||
|
import { getFieldDisplayName } from './fieldState';
|
||||||
|
import { formatLabels } from '../utils/labels';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is created often, and only used when tmplates exist. Using a proxy lets us delay
|
||||||
|
* calculations of the more complex structures (label names) until they are actually used
|
||||||
|
*/
|
||||||
|
export function getTemplateProxyForField(field: Field, frame?: DataFrame, frames?: DataFrame[]): any {
|
||||||
|
return new Proxy(
|
||||||
|
{} as any, // This object shows up in test snapshots
|
||||||
|
{
|
||||||
|
get: (obj: Field, key: string, reciever: any) => {
|
||||||
|
if (key === 'name') {
|
||||||
|
return field.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'displayName') {
|
||||||
|
return getFieldDisplayName(field, frame, frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'labels' || key === 'formattedLabels') {
|
||||||
|
// formattedLabels deprecated
|
||||||
|
if (!field.labels) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...field.labels,
|
||||||
|
__values: Object.values(field.labels)
|
||||||
|
.sort()
|
||||||
|
.join(', '),
|
||||||
|
toString: () => {
|
||||||
|
return formatLabels(field.labels!, '', true);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined; // (field as any)[key]; // any property?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,10 +8,11 @@ import { filterFramesByRefIdTransformer } from './transformers/filterByRefId';
|
|||||||
import { orderFieldsTransformer } from './transformers/order';
|
import { orderFieldsTransformer } from './transformers/order';
|
||||||
import { organizeFieldsTransformer } from './transformers/organize';
|
import { organizeFieldsTransformer } from './transformers/organize';
|
||||||
import { seriesToColumnsTransformer } from './transformers/seriesToColumns';
|
import { seriesToColumnsTransformer } from './transformers/seriesToColumns';
|
||||||
|
import { seriesToRowsTransformer } from './transformers/seriesToRows';
|
||||||
import { renameFieldsTransformer } from './transformers/rename';
|
import { renameFieldsTransformer } from './transformers/rename';
|
||||||
import { labelsToFieldsTransformer } from './transformers/labelsToFields';
|
import { labelsToFieldsTransformer } from './transformers/labelsToFields';
|
||||||
import { ensureColumnsTransformer } from './transformers/ensureColumns';
|
import { ensureColumnsTransformer } from './transformers/ensureColumns';
|
||||||
import { mergeTransformer } from './transformers/merge/merge';
|
import { mergeTransformer } from './transformers/merge';
|
||||||
|
|
||||||
export const standardTransformers = {
|
export const standardTransformers = {
|
||||||
noopTransformer,
|
noopTransformer,
|
||||||
@@ -25,6 +26,7 @@ export const standardTransformers = {
|
|||||||
reduceTransformer,
|
reduceTransformer,
|
||||||
calculateFieldTransformer,
|
calculateFieldTransformer,
|
||||||
seriesToColumnsTransformer,
|
seriesToColumnsTransformer,
|
||||||
|
seriesToRowsTransformer,
|
||||||
renameFieldsTransformer,
|
renameFieldsTransformer,
|
||||||
labelsToFieldsTransformer,
|
labelsToFieldsTransformer,
|
||||||
ensureColumnsTransformer,
|
ensureColumnsTransformer,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export enum DataTransformerID {
|
|||||||
rename = 'rename',
|
rename = 'rename',
|
||||||
calculateField = 'calculateField',
|
calculateField = 'calculateField',
|
||||||
seriesToColumns = 'seriesToColumns',
|
seriesToColumns = 'seriesToColumns',
|
||||||
|
seriesToRows = 'seriesToRows',
|
||||||
merge = 'merge',
|
merge = 'merge',
|
||||||
labelsToFields = 'labelsToFields',
|
labelsToFields = 'labelsToFields',
|
||||||
filterFields = 'filterFields',
|
filterFields = 'filterFields',
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { mockTransformationsRegistry } from '../../../utils/tests/mockTransformationsRegistry';
|
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||||
import { DataTransformerConfig, Field, FieldType } from '../../../types';
|
import { DataTransformerConfig, Field, FieldType } from '../../types';
|
||||||
import { DataTransformerID } from '../ids';
|
import { DataTransformerID } from './ids';
|
||||||
import { toDataFrame } from '../../../dataframe';
|
import { toDataFrame } from '../../dataframe';
|
||||||
import { transformDataFrame } from '../../transformDataFrame';
|
import { transformDataFrame } from '../transformDataFrame';
|
||||||
import { ArrayVector } from '../../../vector';
|
import { ArrayVector } from '../../vector';
|
||||||
import { mergeTransformer, MergeTransformerOptions } from './merge';
|
import { mergeTransformer, MergeTransformerOptions } from './merge';
|
||||||
|
|
||||||
describe('Merge multipe to single', () => {
|
describe('Merge multipe to single', () => {
|
||||||
@@ -35,12 +35,11 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [seriesA, seriesB]);
|
const result = transformDataFrame([cfg], [seriesA, seriesB]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [1000, 2000]),
|
createField('Time', FieldType.time, [2000, 1000]),
|
||||||
createField('Metric', FieldType.string, ['A', 'B']),
|
createField('Temp', FieldType.number, [-1, 1]),
|
||||||
createField('Value', FieldType.number, [1, -1]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine two series with multiple values into one', () => {
|
it('combine two series with multiple values into one', () => {
|
||||||
@@ -67,12 +66,11 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [seriesA, seriesB]);
|
const result = transformDataFrame([cfg], [seriesA, seriesB]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [100, 100, 125, 126, 150, 200]),
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
createField('Metric', FieldType.string, ['A', 'B', 'B', 'B', 'A', 'A']),
|
createField('Temp', FieldType.number, [5, 4, 3, 2, 1, -1]),
|
||||||
createField('Value', FieldType.number, [1, -1, 2, 3, 4, 5]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine three series into one', () => {
|
it('combine three series into one', () => {
|
||||||
@@ -107,12 +105,11 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [seriesA, seriesB, seriesC]);
|
const result = transformDataFrame([cfg], [seriesA, seriesB, seriesC]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [500, 1000, 2000]),
|
createField('Time', FieldType.time, [2000, 1000, 500]),
|
||||||
createField('Metric', FieldType.string, ['C', 'A', 'B']),
|
createField('Temp', FieldType.number, [-1, 1, 2]),
|
||||||
createField('Value', FieldType.number, [2, 1, -1]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine one serie and two tables into one table', () => {
|
it('combine one serie and two tables into one table', () => {
|
||||||
@@ -149,13 +146,12 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [tableA, seriesB, tableB]);
|
const result = transformDataFrame([cfg], [tableA, seriesB, tableB]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [500, 1000, 1000]),
|
createField('Time', FieldType.time, [1000, 1000, 500]),
|
||||||
createField('Metric', FieldType.string, ['C', 'A', 'B']),
|
createField('Temp', FieldType.number, [1, -1, 2]),
|
||||||
createField('Temp', FieldType.number, [2, 1, -1]),
|
createField('Humidity', FieldType.number, [10, null, 5]),
|
||||||
createField('Humidity', FieldType.number, [5, 10, null]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine one serie and two tables with ISO dates into one table', () => {
|
it('combine one serie and two tables with ISO dates into one table', () => {
|
||||||
@@ -192,13 +188,12 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [tableA, seriesB, tableC]);
|
const result = transformDataFrame([cfg], [tableA, seriesB, tableC]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, ['2019-09-01T11:10:23Z', '2019-10-01T11:10:23Z', '2019-11-01T11:10:23Z']),
|
createField('Time', FieldType.time, ['2019-11-01T11:10:23Z', '2019-10-01T11:10:23Z', '2019-09-01T11:10:23Z']),
|
||||||
createField('Metric', FieldType.string, ['B', 'A', 'C']),
|
createField('Temp', FieldType.number, [2, 1, -1]),
|
||||||
createField('Temp', FieldType.number, [-1, 1, 2]),
|
createField('Humidity', FieldType.number, [5, 10, null]),
|
||||||
createField('Humidity', FieldType.number, [null, 10, 5]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine three tables with multiple values into one', () => {
|
it('combine three tables with multiple values into one', () => {
|
||||||
@@ -235,14 +230,15 @@ describe('Merge multipe to single', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const result = transformDataFrame([cfg], [tableA, tableB, tableC]);
|
const result = transformDataFrame([cfg], [tableA, tableB, tableC]);
|
||||||
|
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [100, 100, 100, 124, 125, 126, 149, 150, 200]),
|
createField('Time', FieldType.time, [200, 150, 149, 126, 125, 124, 100, 100, 100]),
|
||||||
createField('Temp', FieldType.number, [1, -1, 1, 4, 2, 3, 5, 4, 5]),
|
createField('Temp', FieldType.number, [5, 4, 5, 3, 2, 4, 1, -1, 1]),
|
||||||
createField('Humidity', FieldType.number, [10, null, 22, 25, null, null, 30, 14, 55]),
|
createField('Humidity', FieldType.number, [55, 14, 30, null, null, 25, 10, null, 22]),
|
||||||
createField('Enabled', FieldType.boolean, [null, true, null, null, false, true, null, null, null]),
|
createField('Enabled', FieldType.boolean, [null, null, null, true, false, null, null, true, null]),
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine two time series, where first serie fields has displayName, into one', () => {
|
it('combine two time series, where first serie fields has displayName, into one', () => {
|
||||||
@@ -269,13 +265,14 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [serieA, serieB]);
|
const result = transformDataFrame([cfg], [serieA, serieB]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [100, 100, 125, 126, 150, 200]),
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
createField('Metric', FieldType.string, ['A', 'B', 'B', 'B', 'A', 'A']),
|
createField('Temp', FieldType.number, [5, 4, 3, 2, 1, -1]),
|
||||||
createField('Value', FieldType.number, [1, -1, 2, 3, 4, 5]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields[2].config).toEqual({});
|
const fields = unwrap(result[0].fields);
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
|
||||||
|
expect(fields[1].config).toEqual({});
|
||||||
|
expect(fields).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine two time series, where first serie fields has units, into one', () => {
|
it('combine two time series, where first serie fields has units, into one', () => {
|
||||||
@@ -302,13 +299,14 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [serieA, serieB]);
|
const result = transformDataFrame([cfg], [serieA, serieB]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [100, 100, 125, 126, 150, 200]),
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
createField('Metric', FieldType.string, ['A', 'B', 'B', 'B', 'A', 'A']),
|
createField('Temp', FieldType.number, [5, 4, 3, 2, 1, -1], { units: 'celsius' }),
|
||||||
createField('Value', FieldType.number, [1, -1, 2, 3, 4, 5], { units: 'celsius' }),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields[2].config).toEqual({ units: 'celsius' });
|
const fields = unwrap(result[0].fields);
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
|
||||||
|
expect(fields[1].config).toEqual({ units: 'celsius' });
|
||||||
|
expect(fields).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('combine two time series, where second serie fields has units, into one', () => {
|
it('combine two time series, where second serie fields has units, into one', () => {
|
||||||
@@ -335,16 +333,28 @@ describe('Merge multipe to single', () => {
|
|||||||
|
|
||||||
const result = transformDataFrame([cfg], [serieA, serieB]);
|
const result = transformDataFrame([cfg], [serieA, serieB]);
|
||||||
const expected: Field[] = [
|
const expected: Field[] = [
|
||||||
createField('Time', FieldType.time, [100, 100, 125, 126, 150, 200]),
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
createField('Metric', FieldType.string, ['A', 'B', 'B', 'B', 'A', 'A']),
|
createField('Temp', FieldType.number, [5, 4, 3, 2, 1, -1]),
|
||||||
createField('Value', FieldType.number, [1, -1, 2, 3, 4, 5]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(result[0].fields[2].config).toEqual({});
|
const fields = unwrap(result[0].fields);
|
||||||
expect(result[0].fields).toMatchObject(expected);
|
|
||||||
|
expect(fields[1].config).toEqual({});
|
||||||
|
expect(fields).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const createField = (name: string, type: FieldType, values: any[], config = {}): Field => {
|
const createField = (name: string, type: FieldType, values: any[], config = {}): Field => {
|
||||||
return { name, type, values: new ArrayVector(values), config, labels: undefined };
|
return { name, type, values: new ArrayVector(values), config, labels: undefined };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unwrap = (fields: Field[]): Field[] => {
|
||||||
|
return fields.map(field =>
|
||||||
|
createField(
|
||||||
|
field.name,
|
||||||
|
field.type,
|
||||||
|
field.values.toArray().map((value: any) => value),
|
||||||
|
field.config
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
216
packages/grafana-data/src/transformations/transformers/merge.ts
Normal file
216
packages/grafana-data/src/transformations/transformers/merge.ts
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
import { DataTransformerID } from './ids';
|
||||||
|
import { DataTransformerInfo } from '../../types/transformations';
|
||||||
|
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
|
||||||
|
import { omit } from 'lodash';
|
||||||
|
import { ArrayVector } from '../../vector/ArrayVector';
|
||||||
|
import { MutableDataFrame, sortDataFrame } from '../../dataframe';
|
||||||
|
|
||||||
|
type MergeDetailsKeyFactory = (existing: Record<string, any>, value: Record<string, any>) => string;
|
||||||
|
|
||||||
|
export interface MergeTransformerOptions {}
|
||||||
|
|
||||||
|
export const mergeTransformer: DataTransformerInfo<MergeTransformerOptions> = {
|
||||||
|
id: DataTransformerID.merge,
|
||||||
|
name: 'Merge series/tables',
|
||||||
|
description: 'Merges multiple series/tables into a single serie/table',
|
||||||
|
defaultOptions: {},
|
||||||
|
transformer: (options: MergeTransformerOptions) => {
|
||||||
|
return (data: DataFrame[]) => {
|
||||||
|
if (!Array.isArray(data) || data.length <= 1) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldByName = new Set<string>();
|
||||||
|
const fieldIndexByName: Record<string, Record<number, number>> = {};
|
||||||
|
const fieldNamesForKey: string[] = [];
|
||||||
|
const dataFrame = new MutableDataFrame();
|
||||||
|
|
||||||
|
for (let frameIndex = 0; frameIndex < data.length; frameIndex++) {
|
||||||
|
const frame = data[frameIndex];
|
||||||
|
|
||||||
|
for (let fieldIndex = 0; fieldIndex < frame.fields.length; fieldIndex++) {
|
||||||
|
const field = frame.fields[fieldIndex];
|
||||||
|
|
||||||
|
if (!fieldByName.has(field.name)) {
|
||||||
|
dataFrame.addField(copyFieldStructure(field));
|
||||||
|
fieldByName.add(field.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldIndexByName[field.name] = fieldIndexByName[field.name] || {};
|
||||||
|
fieldIndexByName[field.name][frameIndex] = fieldIndex;
|
||||||
|
|
||||||
|
if (data.length - 1 !== frameIndex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(fieldIndexByName[field.name]).length === data.length) {
|
||||||
|
fieldNamesForKey.push(field.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldNamesForKey.length === 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataFrameIndexByKey: Record<string, number> = {};
|
||||||
|
const keyFactory = createKeyFactory(data, fieldIndexByName, fieldNamesForKey);
|
||||||
|
const detailsKeyFactory = createDetailsKeyFactory(fieldByName, fieldNamesForKey);
|
||||||
|
const valueMapper = createValueMapper(data, fieldByName, fieldIndexByName);
|
||||||
|
|
||||||
|
for (let frameIndex = 0; frameIndex < data.length; frameIndex++) {
|
||||||
|
const frame = data[frameIndex];
|
||||||
|
|
||||||
|
for (let valueIndex = 0; valueIndex < frame.length; valueIndex++) {
|
||||||
|
const key = keyFactory(frameIndex, valueIndex);
|
||||||
|
const value = valueMapper(frameIndex, valueIndex);
|
||||||
|
mergeOrAdd(key, value, dataFrame, dataFrameIndexByKey, detailsKeyFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeIndex = dataFrame.fields.findIndex(field => field.type === FieldType.time);
|
||||||
|
if (typeof timeIndex === 'number') {
|
||||||
|
return [sortDataFrame(dataFrame, timeIndex, true)];
|
||||||
|
}
|
||||||
|
return [dataFrame];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyFieldStructure = (field: Field): Field => {
|
||||||
|
return {
|
||||||
|
...omit(field, ['values', 'state', 'labels', 'config']),
|
||||||
|
values: new ArrayVector(),
|
||||||
|
config: {
|
||||||
|
...omit(field.config, 'displayName'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createKeyFactory = (
|
||||||
|
data: DataFrame[],
|
||||||
|
fieldPointerByName: Record<string, Record<string, number>>,
|
||||||
|
keyFieldNames: string[]
|
||||||
|
) => {
|
||||||
|
const factoryIndex = keyFieldNames.reduce((index: Record<string, number[]>, fieldName) => {
|
||||||
|
return Object.keys(fieldPointerByName[fieldName]).reduce((index: Record<string, number[]>, frameIndex) => {
|
||||||
|
index[frameIndex] = index[frameIndex] || [];
|
||||||
|
index[frameIndex].push(fieldPointerByName[fieldName][frameIndex]);
|
||||||
|
return index;
|
||||||
|
}, index);
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return (frameIndex: number, valueIndex: number): string => {
|
||||||
|
return factoryIndex[frameIndex].reduce((key: string, fieldIndex: number) => {
|
||||||
|
return key + data[frameIndex].fields[fieldIndex].values.get(valueIndex);
|
||||||
|
}, '');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createDetailsKeyFactory = (fieldByName: Set<string>, fieldNamesForKey: string[]): MergeDetailsKeyFactory => {
|
||||||
|
const fieldNamesToExclude = fieldNamesForKey.reduce((exclude: Record<string, boolean>, fieldName: string) => {
|
||||||
|
exclude[fieldName] = true;
|
||||||
|
return exclude;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const checkOrder = Array.from(fieldByName).filter(fieldName => !fieldNamesToExclude[fieldName]);
|
||||||
|
|
||||||
|
return (existing: Record<string, any>, value: Record<string, any>) => {
|
||||||
|
return checkOrder.reduce((key: string, fieldName: string) => {
|
||||||
|
if (typeof existing[fieldName] === 'undefined') {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
if (typeof value[fieldName] === 'undefined') {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
if (existing[fieldName] === value[fieldName]) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
return key + value[fieldName];
|
||||||
|
}, '');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createValueMapper = (
|
||||||
|
data: DataFrame[],
|
||||||
|
fieldByName: Set<string>,
|
||||||
|
fieldIndexByName: Record<string, Record<number, number>>
|
||||||
|
) => {
|
||||||
|
return (frameIndex: number, valueIndex: number) => {
|
||||||
|
const value: Record<string, any> = {};
|
||||||
|
const fieldNames = Array.from(fieldByName);
|
||||||
|
|
||||||
|
for (const fieldName of fieldNames) {
|
||||||
|
const fieldIndexByFrameIndex = fieldIndexByName[fieldName];
|
||||||
|
if (!fieldIndexByFrameIndex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldIndex = fieldIndexByFrameIndex[frameIndex];
|
||||||
|
if (typeof fieldIndex !== 'number') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const frame = data[frameIndex];
|
||||||
|
if (!frame || !frame.fields) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const field = frame.fields[fieldIndex];
|
||||||
|
if (!field || !field.values) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
value[fieldName] = field.values.get(valueIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMergable = (existing: Record<string, any>, value: Record<string, any>): boolean => {
|
||||||
|
let mergable = true;
|
||||||
|
|
||||||
|
for (const prop in value) {
|
||||||
|
if (typeof existing[prop] === 'undefined') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing[prop] === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing[prop] !== value[prop]) {
|
||||||
|
mergable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergable;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeOrAdd = (
|
||||||
|
key: string,
|
||||||
|
value: Record<string, any>,
|
||||||
|
dataFrame: MutableDataFrame,
|
||||||
|
dataFrameIndexByKey: Record<string, number>,
|
||||||
|
detailsKeyFactory: MergeDetailsKeyFactory
|
||||||
|
) => {
|
||||||
|
if (typeof dataFrameIndexByKey[key] === 'undefined') {
|
||||||
|
dataFrame.add(value);
|
||||||
|
dataFrameIndexByKey[key] = dataFrame.length - 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataFrameIndex = dataFrameIndexByKey[key];
|
||||||
|
const existing = dataFrame.get(dataFrameIndex);
|
||||||
|
|
||||||
|
if (isMergable(existing, value)) {
|
||||||
|
const merged = { ...existing, ...value };
|
||||||
|
dataFrame.set(dataFrameIndex, merged);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextKey = key + detailsKeyFactory(existing, value);
|
||||||
|
mergeOrAdd(nextKey, value, dataFrame, dataFrameIndexByKey, detailsKeyFactory);
|
||||||
|
};
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
import { MutableDataFrame } from '../../../dataframe';
|
|
||||||
import {
|
|
||||||
DataFrame,
|
|
||||||
FieldType,
|
|
||||||
Field,
|
|
||||||
TIME_SERIES_TIME_FIELD_NAME,
|
|
||||||
TIME_SERIES_VALUE_FIELD_NAME,
|
|
||||||
} from '../../../types/dataFrame';
|
|
||||||
import { ArrayVector } from '../../../vector';
|
|
||||||
import { omit } from 'lodash';
|
|
||||||
import { getFrameDisplayName } from '../../../field';
|
|
||||||
|
|
||||||
interface DataFrameBuilderResult {
|
|
||||||
dataFrame: MutableDataFrame;
|
|
||||||
valueMapper: ValueMapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValueMapper = (frame: DataFrame, valueIndex: number, timeIndex: number) => Record<string, any>;
|
|
||||||
|
|
||||||
const TIME_SERIES_METRIC_FIELD_NAME = 'Metric';
|
|
||||||
|
|
||||||
export class DataFrameBuilder {
|
|
||||||
private isOnlyTimeSeries: boolean;
|
|
||||||
private displayMetricField: boolean;
|
|
||||||
private valueFields: Record<string, Field>;
|
|
||||||
private timeField: Field | null;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.isOnlyTimeSeries = true;
|
|
||||||
this.displayMetricField = false;
|
|
||||||
this.valueFields = {};
|
|
||||||
this.timeField = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
addFields(frame: DataFrame, timeIndex: number): void {
|
|
||||||
if (frame.fields.length > 2) {
|
|
||||||
this.isOnlyTimeSeries = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame.fields.length === 2) {
|
|
||||||
this.displayMetricField = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let index = 0; index < frame.fields.length; index++) {
|
|
||||||
const field = frame.fields[index];
|
|
||||||
|
|
||||||
if (index === timeIndex) {
|
|
||||||
if (!this.timeField) {
|
|
||||||
this.timeField = this.copyStructure(field, TIME_SERIES_TIME_FIELD_NAME);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.valueFields[field.name]) {
|
|
||||||
this.valueFields[field.name] = this.copyStructure(field, field.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build(): DataFrameBuilderResult {
|
|
||||||
return {
|
|
||||||
dataFrame: this.createDataFrame(),
|
|
||||||
valueMapper: this.createValueMapper(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private createValueMapper(): ValueMapper {
|
|
||||||
return (frame: DataFrame, valueIndex: number, timeIndex: number) => {
|
|
||||||
return frame.fields.reduce((values: Record<string, any>, field, index) => {
|
|
||||||
const value = field.values.get(valueIndex);
|
|
||||||
|
|
||||||
if (index === timeIndex) {
|
|
||||||
values[TIME_SERIES_TIME_FIELD_NAME] = value;
|
|
||||||
|
|
||||||
if (this.displayMetricField) {
|
|
||||||
values[TIME_SERIES_METRIC_FIELD_NAME] = getFrameDisplayName(frame);
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isOnlyTimeSeries) {
|
|
||||||
values[TIME_SERIES_VALUE_FIELD_NAME] = value;
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
values[field.name] = value;
|
|
||||||
return values;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private createDataFrame(): MutableDataFrame {
|
|
||||||
const dataFrame = new MutableDataFrame();
|
|
||||||
|
|
||||||
if (this.timeField) {
|
|
||||||
dataFrame.addField(this.timeField);
|
|
||||||
|
|
||||||
if (this.displayMetricField) {
|
|
||||||
dataFrame.addField({
|
|
||||||
name: TIME_SERIES_METRIC_FIELD_NAME,
|
|
||||||
type: FieldType.string,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueFields = Object.values(this.valueFields);
|
|
||||||
|
|
||||||
if (this.isOnlyTimeSeries) {
|
|
||||||
if (valueFields.length > 0) {
|
|
||||||
dataFrame.addField({
|
|
||||||
...valueFields[0],
|
|
||||||
name: TIME_SERIES_VALUE_FIELD_NAME,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return dataFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const field of valueFields) {
|
|
||||||
dataFrame.addField(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
private copyStructure(field: Field, name: string): Field {
|
|
||||||
return {
|
|
||||||
...omit(field, ['values', 'name', 'state', 'labels', 'config']),
|
|
||||||
name,
|
|
||||||
values: new ArrayVector(),
|
|
||||||
config: {
|
|
||||||
...omit(field.config, 'displayName'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import { DataFrame } from '../../../types/dataFrame';
|
|
||||||
import { timeComparer } from '../../../field/fieldComparers';
|
|
||||||
import { sortDataFrame } from '../../../dataframe';
|
|
||||||
import { TimeFieldsByFrame } from './TimeFieldsByFrame';
|
|
||||||
|
|
||||||
interface DataFrameStackValue {
|
|
||||||
valueIndex: number;
|
|
||||||
timeIndex: number;
|
|
||||||
frame: DataFrame;
|
|
||||||
}
|
|
||||||
export class DataFramesStackedByTime {
|
|
||||||
private valuesPointerByFrame: Record<number, number>;
|
|
||||||
private dataFrames: DataFrame[];
|
|
||||||
private isSorted: boolean;
|
|
||||||
|
|
||||||
constructor(private timeFields: TimeFieldsByFrame) {
|
|
||||||
this.valuesPointerByFrame = {};
|
|
||||||
this.dataFrames = [];
|
|
||||||
this.isSorted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
push(frame: DataFrame): number {
|
|
||||||
const index = this.dataFrames.length;
|
|
||||||
this.valuesPointerByFrame[index] = 0;
|
|
||||||
this.dataFrames.push(frame);
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
pop(): DataFrameStackValue {
|
|
||||||
if (!this.isSorted) {
|
|
||||||
this.sortByTime();
|
|
||||||
this.isSorted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const frameIndex = this.dataFrames.reduce((champion, frame, index) => {
|
|
||||||
const championTime = this.peekTimeValueForFrame(champion);
|
|
||||||
const contenderTime = this.peekTimeValueForFrame(index);
|
|
||||||
return timeComparer(contenderTime, championTime) >= 0 ? champion : index;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
const previousPointer = this.movePointerForward(frameIndex);
|
|
||||||
|
|
||||||
return {
|
|
||||||
frame: this.dataFrames[frameIndex],
|
|
||||||
valueIndex: previousPointer,
|
|
||||||
timeIndex: this.timeFields.getFieldIndex(frameIndex),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getLength(): number {
|
|
||||||
const frames = Object.values(this.dataFrames);
|
|
||||||
return frames.reduce((length: number, frame) => (length += frame.length), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private peekTimeValueForFrame(frameIndex: number): any {
|
|
||||||
const timeField = this.timeFields.getField(frameIndex);
|
|
||||||
const valuePointer = this.valuesPointerByFrame[frameIndex];
|
|
||||||
return timeField.values.get(valuePointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private movePointerForward(frameIndex: number): number {
|
|
||||||
const currentPointer = this.valuesPointerByFrame[frameIndex];
|
|
||||||
this.valuesPointerByFrame[frameIndex] = currentPointer + 1;
|
|
||||||
|
|
||||||
return currentPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sortByTime() {
|
|
||||||
this.dataFrames = this.dataFrames.map((frame, index) => {
|
|
||||||
const timeFieldIndex = this.timeFields.getFieldIndex(index);
|
|
||||||
return sortDataFrame(frame, timeFieldIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { isNumber } from 'lodash';
|
|
||||||
import { Field, DataFrame } from '../../../types/dataFrame';
|
|
||||||
import { getTimeField } from '../../../dataframe';
|
|
||||||
|
|
||||||
export class TimeFieldsByFrame {
|
|
||||||
private timeIndexByFrameIndex: Record<number, number>;
|
|
||||||
private timeFieldByFrameIndex: Record<number, Field>;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.timeIndexByFrameIndex = {};
|
|
||||||
this.timeFieldByFrameIndex = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
add(frameIndex: number, frame: DataFrame): void {
|
|
||||||
const fieldDescription = getTimeField(frame);
|
|
||||||
const timeIndex = fieldDescription?.timeIndex;
|
|
||||||
const timeField = fieldDescription?.timeField;
|
|
||||||
|
|
||||||
if (isNumber(timeIndex)) {
|
|
||||||
this.timeIndexByFrameIndex[frameIndex] = timeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeField) {
|
|
||||||
this.timeFieldByFrameIndex[frameIndex] = timeField;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getField(frameIndex: number): Field {
|
|
||||||
return this.timeFieldByFrameIndex[frameIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
getFieldIndex(frameIndex: number): number {
|
|
||||||
return this.timeIndexByFrameIndex[frameIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
getLength() {
|
|
||||||
return Object.keys(this.timeIndexByFrameIndex).length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { DataTransformerID } from '../ids';
|
|
||||||
import { DataTransformerInfo } from '../../../types/transformations';
|
|
||||||
import { DataFrame } from '../../../types/dataFrame';
|
|
||||||
import { DataFrameBuilder } from './DataFrameBuilder';
|
|
||||||
import { TimeFieldsByFrame } from './TimeFieldsByFrame';
|
|
||||||
import { DataFramesStackedByTime } from './DataFramesStackedByTime';
|
|
||||||
|
|
||||||
export interface MergeTransformerOptions {}
|
|
||||||
|
|
||||||
export const mergeTransformer: DataTransformerInfo<MergeTransformerOptions> = {
|
|
||||||
id: DataTransformerID.merge,
|
|
||||||
name: 'Merge series/tables',
|
|
||||||
description: 'Merges multiple series/tables by time into a single serie/table',
|
|
||||||
defaultOptions: {},
|
|
||||||
transformer: (options: MergeTransformerOptions) => {
|
|
||||||
return (data: DataFrame[]) => {
|
|
||||||
if (!Array.isArray(data) || data.length <= 1) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeFields = new TimeFieldsByFrame();
|
|
||||||
const framesStack = new DataFramesStackedByTime(timeFields);
|
|
||||||
const dataFrameBuilder = new DataFrameBuilder();
|
|
||||||
|
|
||||||
for (const frame of data) {
|
|
||||||
const frameIndex = framesStack.push(frame);
|
|
||||||
timeFields.add(frameIndex, frame);
|
|
||||||
|
|
||||||
const timeIndex = timeFields.getFieldIndex(frameIndex);
|
|
||||||
dataFrameBuilder.addFields(frame, timeIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length !== timeFields.getLength()) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { dataFrame, valueMapper } = dataFrameBuilder.build();
|
|
||||||
|
|
||||||
for (let index = 0; index < framesStack.getLength(); index++) {
|
|
||||||
const { frame, valueIndex, timeIndex } = framesStack.pop();
|
|
||||||
dataFrame.add(valueMapper(frame, valueIndex, timeIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [dataFrame];
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||||
|
import { DataTransformerConfig, Field, FieldType } from '../../types';
|
||||||
|
import { DataTransformerID } from './ids';
|
||||||
|
import { toDataFrame } from '../../dataframe';
|
||||||
|
import { transformDataFrame } from '../transformDataFrame';
|
||||||
|
import { ArrayVector } from '../../vector';
|
||||||
|
import { seriesToRowsTransformer, SeriesToRowsTransformerOptions } from './seriesToRows';
|
||||||
|
|
||||||
|
describe('Series to rows', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
mockTransformationsRegistry([seriesToRowsTransformer]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine two series into one', () => {
|
||||||
|
const cfg: DataTransformerConfig<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const seriesA = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [1000] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [1] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesB = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [2000] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [-1] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = transformDataFrame([cfg], [seriesA, seriesB]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
createField('Time', FieldType.time, [2000, 1000]),
|
||||||
|
createField('Metric', FieldType.string, ['B', 'A']),
|
||||||
|
createField('Value', FieldType.number, [-1, 1]),
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine two series with multiple values into one', () => {
|
||||||
|
const cfg: DataTransformerConfig<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const seriesA = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 150, 200] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [1, 4, 5] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesB = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 125, 126] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [-1, 2, 3] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = transformDataFrame([cfg], [seriesA, seriesB]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
|
createField('Metric', FieldType.string, ['A', 'A', 'B', 'B', 'A', 'B']),
|
||||||
|
createField('Value', FieldType.number, [5, 4, 3, 2, 1, -1]),
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine three series into one', () => {
|
||||||
|
const cfg: DataTransformerConfig<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const seriesA = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [1000] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [1] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesB = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [2000] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [-1] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesC = toDataFrame({
|
||||||
|
name: 'C',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [500] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [2] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = transformDataFrame([cfg], [seriesA, seriesB, seriesC]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
createField('Time', FieldType.time, [2000, 1000, 500]),
|
||||||
|
createField('Metric', FieldType.string, ['B', 'A', 'C']),
|
||||||
|
createField('Value', FieldType.number, [-1, 1, 2]),
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(unwrap(result[0].fields)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine two time series, where first serie fields has displayName, into one', () => {
|
||||||
|
const cfg: DataTransformerConfig<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const serieA = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 150, 200], config: { displayName: 'Random time' } },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [1, 4, 5], config: { displayName: 'Temp' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const serieB = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 125, 126] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [-1, 2, 3] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = transformDataFrame([cfg], [serieA, serieB]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
|
createField('Metric', FieldType.string, ['A', 'A', 'B', 'B', 'A', 'B']),
|
||||||
|
createField('Value', FieldType.number, [5, 4, 3, 2, 1, -1]),
|
||||||
|
];
|
||||||
|
|
||||||
|
const fields = unwrap(result[0].fields);
|
||||||
|
|
||||||
|
expect(fields[2].config).toEqual({});
|
||||||
|
expect(fields).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine two time series, where first serie fields has units, into one', () => {
|
||||||
|
const cfg: DataTransformerConfig<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const serieA = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 150, 200] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [1, 4, 5], config: { units: 'celsius' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const serieB = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 125, 126] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [-1, 2, 3] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = transformDataFrame([cfg], [serieA, serieB]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
|
createField('Metric', FieldType.string, ['A', 'A', 'B', 'B', 'A', 'B']),
|
||||||
|
createField('Value', FieldType.number, [5, 4, 3, 2, 1, -1], { units: 'celsius' }),
|
||||||
|
];
|
||||||
|
|
||||||
|
const fields = unwrap(result[0].fields);
|
||||||
|
|
||||||
|
expect(fields[2].config).toEqual({ units: 'celsius' });
|
||||||
|
expect(fields).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine two time series, where second serie fields has units, into one', () => {
|
||||||
|
const cfg: DataTransformerConfig<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const serieA = toDataFrame({
|
||||||
|
name: 'A',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 150, 200] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [1, 4, 5] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const serieB = toDataFrame({
|
||||||
|
name: 'B',
|
||||||
|
fields: [
|
||||||
|
{ name: 'Time', type: FieldType.time, values: [100, 125, 126] },
|
||||||
|
{ name: 'Temp', type: FieldType.number, values: [-1, 2, 3], config: { units: 'celsius' } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = transformDataFrame([cfg], [serieA, serieB]);
|
||||||
|
const expected: Field[] = [
|
||||||
|
createField('Time', FieldType.time, [200, 150, 126, 125, 100, 100]),
|
||||||
|
createField('Metric', FieldType.string, ['A', 'A', 'B', 'B', 'A', 'B']),
|
||||||
|
createField('Value', FieldType.number, [5, 4, 3, 2, 1, -1]),
|
||||||
|
];
|
||||||
|
|
||||||
|
const fields = unwrap(result[0].fields);
|
||||||
|
|
||||||
|
expect(fields[2].config).toEqual({});
|
||||||
|
expect(fields).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const createField = (name: string, type: FieldType, values: any[], config = {}): Field => {
|
||||||
|
return { name, type, values: new ArrayVector(values), config, labels: undefined };
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwrap = (fields: Field[]): Field[] => {
|
||||||
|
return fields.map(field =>
|
||||||
|
createField(
|
||||||
|
field.name,
|
||||||
|
field.type,
|
||||||
|
field.values.toArray().map((value: any) => value),
|
||||||
|
field.config
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { omit } from 'lodash';
|
||||||
|
import { DataTransformerID } from './ids';
|
||||||
|
import { DataTransformerInfo } from '../../types/transformations';
|
||||||
|
import {
|
||||||
|
DataFrame,
|
||||||
|
Field,
|
||||||
|
FieldType,
|
||||||
|
TIME_SERIES_TIME_FIELD_NAME,
|
||||||
|
TIME_SERIES_VALUE_FIELD_NAME,
|
||||||
|
TIME_SERIES_METRIC_FIELD_NAME,
|
||||||
|
} from '../../types/dataFrame';
|
||||||
|
import { isTimeSeries } from '../../dataframe/utils';
|
||||||
|
import { MutableDataFrame, sortDataFrame } from '../../dataframe';
|
||||||
|
import { ArrayVector } from '../../vector';
|
||||||
|
import { getFrameDisplayName } from '../../field/fieldState';
|
||||||
|
|
||||||
|
export interface SeriesToRowsTransformerOptions {}
|
||||||
|
|
||||||
|
export const seriesToRowsTransformer: DataTransformerInfo<SeriesToRowsTransformerOptions> = {
|
||||||
|
id: DataTransformerID.seriesToRows,
|
||||||
|
name: 'Series to rows',
|
||||||
|
description: 'Combines multiple series into a single serie and appends a column with metric name per value.',
|
||||||
|
defaultOptions: {},
|
||||||
|
transformer: (options: SeriesToRowsTransformerOptions) => {
|
||||||
|
return (data: DataFrame[]) => {
|
||||||
|
if (!Array.isArray(data) || data.length <= 1) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTimeSeries(data)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeFieldByIndex: Record<number, number> = {};
|
||||||
|
const targetFields = new Set<string>();
|
||||||
|
const dataFrame = new MutableDataFrame();
|
||||||
|
const metricField: Field = {
|
||||||
|
name: TIME_SERIES_METRIC_FIELD_NAME,
|
||||||
|
values: new ArrayVector(),
|
||||||
|
config: {},
|
||||||
|
type: FieldType.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let frameIndex = 0; frameIndex < data.length; frameIndex++) {
|
||||||
|
const frame = data[frameIndex];
|
||||||
|
|
||||||
|
for (let fieldIndex = 0; fieldIndex < frame.fields.length; fieldIndex++) {
|
||||||
|
const field = frame.fields[fieldIndex];
|
||||||
|
|
||||||
|
if (field.type === FieldType.time) {
|
||||||
|
timeFieldByIndex[frameIndex] = fieldIndex;
|
||||||
|
|
||||||
|
if (!targetFields.has(TIME_SERIES_TIME_FIELD_NAME)) {
|
||||||
|
dataFrame.addField(copyFieldStructure(field, TIME_SERIES_TIME_FIELD_NAME));
|
||||||
|
dataFrame.addField(metricField);
|
||||||
|
targetFields.add(TIME_SERIES_TIME_FIELD_NAME);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetFields.has(TIME_SERIES_VALUE_FIELD_NAME)) {
|
||||||
|
dataFrame.addField(copyFieldStructure(field, TIME_SERIES_VALUE_FIELD_NAME));
|
||||||
|
targetFields.add(TIME_SERIES_VALUE_FIELD_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let frameIndex = 0; frameIndex < data.length; frameIndex++) {
|
||||||
|
const frame = data[frameIndex];
|
||||||
|
|
||||||
|
for (let valueIndex = 0; valueIndex < frame.length; valueIndex++) {
|
||||||
|
const timeFieldIndex = timeFieldByIndex[frameIndex];
|
||||||
|
const valueFieldIndex = timeFieldIndex === 0 ? 1 : 0;
|
||||||
|
|
||||||
|
dataFrame.add({
|
||||||
|
[TIME_SERIES_TIME_FIELD_NAME]: frame.fields[timeFieldIndex].values.get(valueIndex),
|
||||||
|
[TIME_SERIES_METRIC_FIELD_NAME]: getFrameDisplayName(frame),
|
||||||
|
[TIME_SERIES_VALUE_FIELD_NAME]: frame.fields[valueFieldIndex].values.get(valueIndex),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [sortDataFrame(dataFrame, 0, true)];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyFieldStructure = (field: Field, name: string): Field => {
|
||||||
|
return {
|
||||||
|
...omit(field, ['values', 'state', 'labels', 'config', 'name']),
|
||||||
|
name: name,
|
||||||
|
values: new ArrayVector(),
|
||||||
|
config: {
|
||||||
|
...omit(field.config, 'displayName'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -42,6 +42,10 @@ export class AppPlugin<T = KeyValue> extends GrafanaPlugin<AppPluginMeta<T>> {
|
|||||||
/**
|
/**
|
||||||
* Set the component displayed under:
|
* Set the component displayed under:
|
||||||
* /a/${plugin-id}/*
|
* /a/${plugin-id}/*
|
||||||
|
*
|
||||||
|
* If the NavModel is configured, the page will have a managed frame, otheriwse it has full control.
|
||||||
|
*
|
||||||
|
* NOTE: this structure will change in 7.2+ so that it is managed with a normal react router
|
||||||
*/
|
*/
|
||||||
setRootPage(root: ComponentClass<AppRootProps<T>>, rootNav?: NavModel) {
|
setRootPage(root: ComponentClass<AppRootProps<T>>, rootNav?: NavModel) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export enum LoadingState {
|
|||||||
Error = 'Error',
|
Error = 'Error',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PreferredVisualisationType = 'graph' | 'table';
|
export type PreferredVisualisationType = 'graph' | 'table' | 'logs' | 'trace';
|
||||||
|
|
||||||
export interface QueryResultMeta {
|
export interface QueryResultMeta {
|
||||||
/** DatasSource Specific Values */
|
/** DatasSource Specific Values */
|
||||||
@@ -47,6 +47,7 @@ export interface QueryResultMeta {
|
|||||||
searchWords?: string[]; // used by log models and loki
|
searchWords?: string[]; // used by log models and loki
|
||||||
limit?: number; // used by log models and loki
|
limit?: number; // used by log models and loki
|
||||||
json?: boolean; // used to keep track of old json doc values
|
json?: boolean; // used to keep track of old json doc values
|
||||||
|
instant?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryResultMetaStat extends FieldConfig {
|
export interface QueryResultMetaStat extends FieldConfig {
|
||||||
|
|||||||
@@ -150,3 +150,4 @@ export interface FieldCalcs extends Record<string, any> {}
|
|||||||
|
|
||||||
export const TIME_SERIES_VALUE_FIELD_NAME = 'Value';
|
export const TIME_SERIES_VALUE_FIELD_NAME = 'Value';
|
||||||
export const TIME_SERIES_TIME_FIELD_NAME = 'Time';
|
export const TIME_SERIES_TIME_FIELD_NAME = 'Time';
|
||||||
|
export const TIME_SERIES_METRIC_FIELD_NAME = 'Metric';
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export interface DataLink<T extends DataQuery = any> {
|
|||||||
// If dataLink represents internal link this has to be filled. Internal link is defined as a query in a particular
|
// If dataLink represents internal link this has to be filled. Internal link is defined as a query in a particular
|
||||||
// datas ource that we want to show to the user. Usually this results in a link to explore but can also lead to
|
// datas ource that we want to show to the user. Usually this results in a link to explore but can also lead to
|
||||||
// more custom onClick behaviour if needed.
|
// more custom onClick behaviour if needed.
|
||||||
|
// @internal and subject to change in future releases
|
||||||
internal?: {
|
internal?: {
|
||||||
query: T;
|
query: T;
|
||||||
datasourceUid: string;
|
datasourceUid: string;
|
||||||
|
|||||||
@@ -310,7 +310,6 @@ export interface QueryEditorProps<
|
|||||||
* Contains query response filtered by refId of QueryResultBase and possible query error
|
* Contains query response filtered by refId of QueryResultBase and possible query error
|
||||||
*/
|
*/
|
||||||
data?: PanelData;
|
data?: PanelData;
|
||||||
exploreMode?: ExploreMode;
|
|
||||||
exploreId?: any;
|
exploreId?: any;
|
||||||
history?: HistoryItem[];
|
history?: HistoryItem[];
|
||||||
}
|
}
|
||||||
@@ -334,13 +333,11 @@ export interface ExploreQueryFieldProps<
|
|||||||
history: any[];
|
history: any[];
|
||||||
onBlur?: () => void;
|
onBlur?: () => void;
|
||||||
absoluteRange?: AbsoluteTimeRange;
|
absoluteRange?: AbsoluteTimeRange;
|
||||||
exploreMode?: ExploreMode;
|
|
||||||
exploreId?: any;
|
exploreId?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExploreStartPageProps {
|
export interface ExploreStartPageProps {
|
||||||
datasource?: DataSourceApi;
|
datasource?: DataSourceApi;
|
||||||
exploreMode: ExploreMode;
|
|
||||||
onClickExample: (query: DataQuery) => void;
|
onClickExample: (query: DataQuery) => void;
|
||||||
exploreId?: any;
|
exploreId?: any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ExploreMode } from './datasource';
|
|
||||||
import { RawTimeRange } from './time';
|
import { RawTimeRange } from './time';
|
||||||
import { LogsDedupStrategy } from './logs';
|
import { LogsDedupStrategy } from './logs';
|
||||||
|
|
||||||
@@ -6,7 +5,6 @@ import { LogsDedupStrategy } from './logs';
|
|||||||
export interface ExploreUrlState {
|
export interface ExploreUrlState {
|
||||||
datasource: string;
|
datasource: string;
|
||||||
queries: any[]; // Should be a DataQuery, but we're going to strip refIds, so typing makes less sense
|
queries: any[]; // Should be a DataQuery, but we're going to strip refIds, so typing makes less sense
|
||||||
mode: ExploreMode;
|
|
||||||
range: RawTimeRange;
|
range: RawTimeRange;
|
||||||
ui: ExploreUIState;
|
ui: ExploreUIState;
|
||||||
originPanelId?: number;
|
originPanelId?: number;
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ export interface PanelProps<T = any> {
|
|||||||
onChangeTimeRange: (timeRange: AbsoluteTimeRange) => void;
|
onChangeTimeRange: (timeRange: AbsoluteTimeRange) => void;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
renderCounter: number;
|
renderCounter: number;
|
||||||
|
/** Panel title */
|
||||||
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelEditorProps<T = any> {
|
export interface PanelEditorProps<T = any> {
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ export type TraceSpanData = {
|
|||||||
tags?: TraceKeyValuePair[];
|
tags?: TraceKeyValuePair[];
|
||||||
references?: TraceSpanReference[];
|
references?: TraceSpanReference[];
|
||||||
warnings?: string[] | null;
|
warnings?: string[] | null;
|
||||||
|
stackTraces?: string[];
|
||||||
flags: number;
|
flags: number;
|
||||||
|
errorIconColor?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TraceSpan = TraceSpanData & {
|
export type TraceSpan = TraceSpanData & {
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ export enum MappingType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface BaseMap {
|
interface BaseMap {
|
||||||
id: number;
|
id: number; // this could/should just be the array index
|
||||||
operator: string;
|
text: string; // the final display value
|
||||||
text: string;
|
|
||||||
type: MappingType;
|
type: MappingType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ describe('mapInternalLinkToExplore', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: 'testDS',
|
title: 'testDS',
|
||||||
href:
|
href:
|
||||||
'/explore?left={"datasource":"testDS","queries":[{"query":"12344"}],"mode":"Metrics","ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}',
|
'/explore?left={"datasource":"testDS","queries":[{"query":"12344"}],"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}',
|
||||||
onClick: undefined,
|
onClick: undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
DataLink,
|
DataLink,
|
||||||
DataQuery,
|
DataQuery,
|
||||||
DataSourceInstanceSettings,
|
DataSourceInstanceSettings,
|
||||||
ExploreMode,
|
|
||||||
Field,
|
Field,
|
||||||
InterpolateFunction,
|
InterpolateFunction,
|
||||||
LinkModel,
|
LinkModel,
|
||||||
@@ -82,7 +81,6 @@ function generateInternalHref<T extends DataQuery = any>(datasourceName: string,
|
|||||||
queries: [query],
|
queries: [query],
|
||||||
// This should get overwritten if datasource does not support that mode and we do not know what mode is
|
// This should get overwritten if datasource does not support that mode and we do not know what mode is
|
||||||
// preferred anyway.
|
// preferred anyway.
|
||||||
mode: ExploreMode.Metrics,
|
|
||||||
ui: {
|
ui: {
|
||||||
showingGraph: true,
|
showingGraph: true,
|
||||||
showingTable: true,
|
showingTable: true,
|
||||||
|
|||||||
@@ -62,11 +62,14 @@ export function findUniqueLabels(labels: Labels | undefined, commonLabels: Label
|
|||||||
/**
|
/**
|
||||||
* Serializes the given labels to a string.
|
* Serializes the given labels to a string.
|
||||||
*/
|
*/
|
||||||
export function formatLabels(labels: Labels, defaultValue = ''): string {
|
export function formatLabels(labels: Labels, defaultValue = '', withoutBraces?: boolean): string {
|
||||||
if (!labels || Object.keys(labels).length === 0) {
|
if (!labels || Object.keys(labels).length === 0) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
const labelKeys = Object.keys(labels).sort();
|
const labelKeys = Object.keys(labels).sort();
|
||||||
const cleanSelector = labelKeys.map(key => `${key}="${labels[key]}"`).join(', ');
|
const cleanSelector = labelKeys.map(key => `${key}="${labels[key]}"`).join(', ');
|
||||||
|
if (withoutBraces) {
|
||||||
|
return cleanSelector;
|
||||||
|
}
|
||||||
return ['{', cleanSelector, '}'].join('');
|
return ['{', cleanSelector, '}'].join('');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: bo
|
|||||||
urlState.range.to,
|
urlState.range.to,
|
||||||
urlState.datasource,
|
urlState.datasource,
|
||||||
...urlState.queries,
|
...urlState.queries,
|
||||||
{ mode: urlState.mode },
|
|
||||||
{
|
{
|
||||||
ui: [
|
ui: [
|
||||||
!!urlState.ui.showingGraph,
|
!!urlState.ui.showingGraph,
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return undefined with no matching valuemappings', () => {
|
it('should return undefined with no matching valuemappings', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 0, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
{ id: 1, operator: '', text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
|
{ id: 1, text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
|
||||||
];
|
];
|
||||||
const value = '10';
|
const value = '10';
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return first matching mapping with lowest id', () => {
|
it('should return first matching mapping with lowest id', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
||||||
{ id: 1, operator: '', text: 'tio', type: MappingType.ValueToText, value: '10' },
|
{ id: 1, text: 'tio', type: MappingType.ValueToText, value: '10' },
|
||||||
];
|
];
|
||||||
const value = '10';
|
const value = '10';
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return if value is null and value to text mapping value is null', () => {
|
it('should return if value is null and value to text mapping value is null', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
||||||
{ id: 1, operator: '', text: '<NULL>', type: MappingType.ValueToText, value: 'null' },
|
{ id: 1, text: '<NULL>', type: MappingType.ValueToText, value: 'null' },
|
||||||
];
|
];
|
||||||
const value = null;
|
const value = null;
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return if value is null and range to text mapping from and to is null', () => {
|
it('should return if value is null and range to text mapping from and to is null', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '<NULL>', type: MappingType.RangeToText, from: 'null', to: 'null' },
|
{ id: 0, text: '<NULL>', type: MappingType.RangeToText, from: 'null', to: 'null' },
|
||||||
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
];
|
];
|
||||||
const value = null;
|
const value = null;
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return rangeToText mapping where value equals to', () => {
|
it('should return rangeToText mapping where value equals to', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '1-10', type: MappingType.RangeToText, from: '1', to: '10' },
|
{ id: 0, text: '1-10', type: MappingType.RangeToText, from: '1', to: '10' },
|
||||||
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
];
|
];
|
||||||
const value = '10';
|
const value = '10';
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return rangeToText mapping where value equals from', () => {
|
it('should return rangeToText mapping where value equals from', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '10-20', type: MappingType.RangeToText, from: '10', to: '20' },
|
{ id: 0, text: '10-20', type: MappingType.RangeToText, from: '10', to: '20' },
|
||||||
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
];
|
];
|
||||||
const value = '10';
|
const value = '10';
|
||||||
|
|
||||||
@@ -71,8 +71,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should return rangeToText mapping where value is between from and to', () => {
|
it('should return rangeToText mapping where value is between from and to', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
||||||
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
|
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
|
||||||
];
|
];
|
||||||
const value = '10';
|
const value = '10';
|
||||||
|
|
||||||
@@ -81,8 +81,8 @@ describe('Format value with value mappings', () => {
|
|||||||
|
|
||||||
it('should map value text to mapping', () => {
|
it('should map value text to mapping', () => {
|
||||||
const valueMappings: ValueMapping[] = [
|
const valueMappings: ValueMapping[] = [
|
||||||
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
|
||||||
{ id: 1, operator: '', text: 'ELVA', type: MappingType.ValueToText, value: 'elva' },
|
{ id: 1, text: 'ELVA', type: MappingType.ValueToText, value: 'elva' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const value = 'elva';
|
const value = 'elva';
|
||||||
|
|||||||
@@ -290,8 +290,8 @@ export const getCategories = (): ValueFormatCategory[] => [
|
|||||||
{ name: 'Gray (Gy)', id: 'radgy', fn: decimalSIPrefix('Gy') },
|
{ name: 'Gray (Gy)', id: 'radgy', fn: decimalSIPrefix('Gy') },
|
||||||
{ name: 'rad', id: 'radrad', fn: decimalSIPrefix('rad') },
|
{ name: 'rad', id: 'radrad', fn: decimalSIPrefix('rad') },
|
||||||
{ name: 'Sievert (Sv)', id: 'radsv', fn: decimalSIPrefix('Sv') },
|
{ name: 'Sievert (Sv)', id: 'radsv', fn: decimalSIPrefix('Sv') },
|
||||||
{ name: 'milliSievert (mSv)', id: 'radmsv', fn: decimalSIPrefix('mSv', -1) },
|
{ name: 'milliSievert (mSv)', id: 'radmsv', fn: decimalSIPrefix('Sv', -1) },
|
||||||
{ name: 'microSievert (µSv)', id: 'radusv', fn: decimalSIPrefix('µSv', -2) },
|
{ name: 'microSievert (µSv)', id: 'radusv', fn: decimalSIPrefix('Sv', -2) },
|
||||||
{ name: 'rem', id: 'radrem', fn: decimalSIPrefix('rem') },
|
{ name: 'rem', id: 'radrem', fn: decimalSIPrefix('rem') },
|
||||||
{ name: 'Exposure (C/kg)', id: 'radexpckg', fn: decimalSIPrefix('C/kg') },
|
{ name: 'Exposure (C/kg)', id: 'radexpckg', fn: decimalSIPrefix('C/kg') },
|
||||||
{ name: 'roentgen (R)', id: 'radr', fn: decimalSIPrefix('R') },
|
{ name: 'roentgen (R)', id: 'radr', fn: decimalSIPrefix('R') },
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/e2e-selectors",
|
"name": "@grafana/e2e-selectors",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"description": "Grafana End-to-End Test Selectors Library",
|
"description": "Grafana End-to-End Test Selectors Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/e2e",
|
"name": "@grafana/e2e",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"description": "Grafana End-to-End Test Library",
|
"description": "Grafana End-to-End Test Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cypress/webpack-preprocessor": "4.1.3",
|
"@cypress/webpack-preprocessor": "4.1.3",
|
||||||
"@grafana/e2e-selectors": "7.1.0-pre.0",
|
"@grafana/e2e-selectors": "7.1.5",
|
||||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||||
"@mochajs/json-file-reporter": "^1.2.0",
|
"@mochajs/json-file-reporter": "^1.2.0",
|
||||||
"blink-diff": "1.0.13",
|
"blink-diff": "1.0.13",
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ export const addPanel = (config?: Partial<AddPanelConfig>): any =>
|
|||||||
.click();
|
.click();
|
||||||
closeOptionsGroup('type');
|
closeOptionsGroup('type');
|
||||||
|
|
||||||
|
closeOptions();
|
||||||
|
|
||||||
queriesForm(fullConfig);
|
queriesForm(fullConfig);
|
||||||
|
|
||||||
e2e().wait('@chartData');
|
e2e().wait('@chartData');
|
||||||
@@ -77,8 +79,6 @@ export const addPanel = (config?: Partial<AddPanelConfig>): any =>
|
|||||||
//e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content').contains('No data');
|
//e2e.components.Panels.Panel.containerByTitle(panelTitle).find('.panel-content').contains('No data');
|
||||||
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
//e2e.components.QueryEditorRow.actionButton('Disable/enable query').click();
|
||||||
|
|
||||||
closeOptions();
|
|
||||||
|
|
||||||
e2e()
|
e2e()
|
||||||
.get('button[title="Apply changes and go back to dashboard"]')
|
.get('button[title="Apply changes and go back to dashboard"]')
|
||||||
.click();
|
.click();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/runtime",
|
"name": "@grafana/runtime",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"description": "Grafana Runtime Library",
|
"description": "Grafana Runtime Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grafana",
|
"grafana",
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grafana/data": "7.1.0-pre.0",
|
"@grafana/data": "7.1.5",
|
||||||
"@grafana/ui": "7.1.0-pre.0",
|
"@grafana/ui": "7.1.5",
|
||||||
"systemjs": "0.20.19",
|
"systemjs": "0.20.19",
|
||||||
"systemjs-plugin-css": "0.1.37"
|
"systemjs-plugin-css": "0.1.37"
|
||||||
},
|
},
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||||
"@rollup/plugin-commonjs": "11.0.2",
|
"@rollup/plugin-commonjs": "11.0.2",
|
||||||
"@rollup/plugin-node-resolve": "7.1.1",
|
"@rollup/plugin-node-resolve": "7.1.1",
|
||||||
|
"@types/jest": "23.3.14",
|
||||||
"@types/rollup-plugin-visualizer": "2.6.0",
|
"@types/rollup-plugin-visualizer": "2.6.0",
|
||||||
"@types/systemjs": "^0.20.6",
|
"@types/systemjs": "^0.20.6",
|
||||||
"@types/jest": "23.3.14",
|
|
||||||
"lodash": "4.17.15",
|
"lodash": "4.17.15",
|
||||||
"pretty-format": "25.1.0",
|
"pretty-format": "25.1.0",
|
||||||
"rollup": "2.0.6",
|
"rollup": "2.0.6",
|
||||||
|
|||||||
@@ -129,6 +129,11 @@ export class DataSourceWithBackend<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally augment the response before returning the results to the
|
* Optionally augment the response before returning the results to the
|
||||||
|
*
|
||||||
|
* NOTE: this was added in 7.1 for azure, and will be removed in 7.2
|
||||||
|
* when the entire response pipeline is Observable
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
processResponse?(res: DataQueryResponse): Promise<DataQueryResponse>;
|
processResponse?(res: DataQueryResponse): Promise<DataQueryResponse>;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/toolkit",
|
"name": "@grafana/toolkit",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"description": "Grafana Toolkit",
|
"description": "Grafana Toolkit",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grafana",
|
"grafana",
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const packagePluginRunner: TaskRunner<PluginCIOptions> = async () => {
|
|||||||
|
|
||||||
fs.exists(jobsDir, jobsDirExists => {
|
fs.exists(jobsDir, jobsDirExists => {
|
||||||
if (!jobsDirExists) {
|
if (!jobsDirExists) {
|
||||||
throw 'You must run plugin:ci-build prior to running plugin:ci-package';
|
throw new Error('You must run plugin:ci-build prior to running plugin:ci-package');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/ui",
|
"name": "@grafana/ui",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"description": "Grafana Components Library",
|
"description": "Grafana Components Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grafana",
|
"grafana",
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/core": "^10.0.27",
|
"@emotion/core": "^10.0.27",
|
||||||
"@grafana/data": "7.1.0-pre.0",
|
"@grafana/data": "7.1.5",
|
||||||
"@grafana/e2e-selectors": "7.1.0-pre.0",
|
"@grafana/e2e-selectors": "7.1.5",
|
||||||
"@grafana/slate-react": "0.22.9-grafana",
|
"@grafana/slate-react": "0.22.9-grafana",
|
||||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||||
"@iconscout/react-unicons": "^1.0.0",
|
"@iconscout/react-unicons": "^1.0.0",
|
||||||
@@ -47,9 +47,8 @@
|
|||||||
"immutable": "3.8.2",
|
"immutable": "3.8.2",
|
||||||
"jquery": "3.5.1",
|
"jquery": "3.5.1",
|
||||||
"lodash": "4.17.15",
|
"lodash": "4.17.15",
|
||||||
"monaco-editor": "0.20.0",
|
|
||||||
"react-monaco-editor": "0.36.0",
|
|
||||||
"moment": "2.24.0",
|
"moment": "2.24.0",
|
||||||
|
"monaco-editor": "0.20.0",
|
||||||
"papaparse": "4.6.3",
|
"papaparse": "4.6.3",
|
||||||
"rc-cascader": "1.0.1",
|
"rc-cascader": "1.0.1",
|
||||||
"rc-drawer": "3.1.3",
|
"rc-drawer": "3.1.3",
|
||||||
@@ -63,6 +62,7 @@
|
|||||||
"react-dom": "16.12.0",
|
"react-dom": "16.12.0",
|
||||||
"react-highlight-words": "0.16.0",
|
"react-highlight-words": "0.16.0",
|
||||||
"react-hook-form": "5.1.3",
|
"react-hook-form": "5.1.3",
|
||||||
|
"react-monaco-editor": "0.36.0",
|
||||||
"react-popper": "1.3.3",
|
"react-popper": "1.3.3",
|
||||||
"react-storybook-addon-props-combinations": "1.1.0",
|
"react-storybook-addon-props-combinations": "1.1.0",
|
||||||
"react-table": "7.0.0",
|
"react-table": "7.0.0",
|
||||||
|
|||||||
@@ -605,9 +605,6 @@ function getValueStyles(
|
|||||||
styles.paddingRight = `${VALUE_LEFT_PADDING}px`;
|
styles.paddingRight = `${VALUE_LEFT_PADDING}px`;
|
||||||
// Need to remove the left padding from the text width constraints
|
// Need to remove the left padding from the text width constraints
|
||||||
textWidth -= VALUE_LEFT_PADDING;
|
textWidth -= VALUE_LEFT_PADDING;
|
||||||
|
|
||||||
// adjust width of title box
|
|
||||||
styles.width = measureText(formattedValueString, styles.fontSize).width + VALUE_LEFT_PADDING * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return styles;
|
return styles;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ exports[`BarGauge Render with basic options should render 1`] = `
|
|||||||
"lineHeight": 1,
|
"lineHeight": 1,
|
||||||
"paddingLeft": "10px",
|
"paddingLeft": "10px",
|
||||||
"paddingRight": "10px",
|
"paddingRight": "10px",
|
||||||
"width": 22,
|
"width": "60px",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value={
|
value={
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ export interface Props extends Themeable {
|
|||||||
justifyMode?: BigValueJustifyMode;
|
justifyMode?: BigValueJustifyMode;
|
||||||
alignmentFactors?: DisplayValueAlignmentFactors;
|
alignmentFactors?: DisplayValueAlignmentFactors;
|
||||||
textMode?: BigValueTextMode;
|
textMode?: BigValueTextMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If part of a series of stat panes, this is the total number.
|
||||||
|
* Used by BigValueTextMode.Auto text mode.
|
||||||
|
*/
|
||||||
|
count?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BigValue extends PureComponent<Props> {
|
export class BigValue extends PureComponent<Props> {
|
||||||
|
|||||||
@@ -463,12 +463,18 @@ export interface BigValueTextValues extends DisplayValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTextValues(props: Props): BigValueTextValues {
|
function getTextValues(props: Props): BigValueTextValues {
|
||||||
const { textMode: nameAndValue, value, alignmentFactors } = props;
|
const { value, alignmentFactors, count } = props;
|
||||||
|
let { textMode } = props;
|
||||||
|
|
||||||
const titleToAlignTo = alignmentFactors ? alignmentFactors.title : value.title;
|
const titleToAlignTo = alignmentFactors ? alignmentFactors.title : value.title;
|
||||||
const valueToAlignTo = formattedValueToString(alignmentFactors ? alignmentFactors : value);
|
const valueToAlignTo = formattedValueToString(alignmentFactors ? alignmentFactors : value);
|
||||||
|
|
||||||
switch (nameAndValue) {
|
// In the auto case we only show title if this big value is part of more panes (count > 1)
|
||||||
|
if (textMode === BigValueTextMode.Auto && (count ?? 1) === 1) {
|
||||||
|
textMode = BigValueTextMode.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (textMode) {
|
||||||
case BigValueTextMode.Name:
|
case BigValueTextMode.Name:
|
||||||
return {
|
return {
|
||||||
...value,
|
...value,
|
||||||
@@ -498,6 +504,7 @@ function getTextValues(props: Props): BigValueTextValues {
|
|||||||
valueToAlignTo: '1',
|
valueToAlignTo: '1',
|
||||||
tooltip: `Name: ${value.title}\nValue: ${formattedValueToString(value)}`,
|
tooltip: `Name: ${value.title}\nValue: ${formattedValueToString(value)}`,
|
||||||
};
|
};
|
||||||
|
case BigValueTextMode.ValueAndName:
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
...value,
|
...value,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React from 'react';
|
|||||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||||
import { FileUpload } from './FileUpload';
|
import { FileUpload } from './FileUpload';
|
||||||
import mdx from './FileUpload.mdx';
|
import mdx from './FileUpload.mdx';
|
||||||
|
import { useSize } from '../../utils/storybook/useSize';
|
||||||
|
import { ComponentSize } from '../../types/size';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Forms/FileUpload',
|
title: 'Forms/FileUpload',
|
||||||
@@ -15,8 +17,10 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const single = () => {
|
export const single = () => {
|
||||||
|
const size = useSize();
|
||||||
return (
|
return (
|
||||||
<FileUpload
|
<FileUpload
|
||||||
|
size={size as ComponentSize}
|
||||||
onFileUpload={({ currentTarget }) => console.log('file', currentTarget?.files && currentTarget.files[0])}
|
onFileUpload={({ currentTarget }) => console.log('file', currentTarget?.files && currentTarget.files[0])}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import { GrafanaTheme } from '@grafana/data';
|
|||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { getFormStyles, Icon } from '../index';
|
import { getFormStyles, Icon } from '../index';
|
||||||
import { stylesFactory, useTheme } from '../../themes';
|
import { stylesFactory, useTheme } from '../../themes';
|
||||||
|
import { ComponentSize } from '../../types/size';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onFileUpload: (event: FormEvent<HTMLInputElement>) => void;
|
onFileUpload: (event: FormEvent<HTMLInputElement>) => void;
|
||||||
/** Accepted file extensions */
|
/** Accepted file extensions */
|
||||||
accept?: string;
|
accept?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
size?: ComponentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
function trimFileName(fileName: string) {
|
function trimFileName(fileName: string) {
|
||||||
@@ -24,9 +26,15 @@ function trimFileName(fileName: string) {
|
|||||||
return `${file.substring(0, nameLength)}...${extension}`;
|
return `${file.substring(0, nameLength)}...${extension}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FileUpload: FC<Props> = ({ onFileUpload, className, children = 'Upload file', accept = '*' }) => {
|
export const FileUpload: FC<Props> = ({
|
||||||
|
onFileUpload,
|
||||||
|
className,
|
||||||
|
children = 'Upload file',
|
||||||
|
accept = '*',
|
||||||
|
size = 'md',
|
||||||
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const style = getStyles(theme);
|
const style = getStyles(theme, size);
|
||||||
const [fileName, setFileName] = useState('');
|
const [fileName, setFileName] = useState('');
|
||||||
|
|
||||||
const onChange = useCallback((event: FormEvent<HTMLInputElement>) => {
|
const onChange = useCallback((event: FormEvent<HTMLInputElement>) => {
|
||||||
@@ -60,8 +68,8 @@ export const FileUpload: FC<Props> = ({ onFileUpload, className, children = 'Upl
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme, size: ComponentSize) => {
|
||||||
const buttonFormStyle = getFormStyles(theme, { variant: 'primary', invalid: false, size: 'md' }).button.button;
|
const buttonFormStyle = getFormStyles(theme, { variant: 'primary', invalid: false, size }).button.button;
|
||||||
return {
|
return {
|
||||||
fileUpload: css`
|
fileUpload: css`
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ export const FormLabel: FunctionComponent<Props> = ({
|
|||||||
{tooltip && (
|
{tooltip && (
|
||||||
<Tooltip placement="top" content={tooltip} theme={'info'}>
|
<Tooltip placement="top" content={tooltip} theme={'info'}>
|
||||||
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
||||||
<Icon name="info-circle" size="xs" style={{ marginLeft: '10px' }} />
|
<Icon name="info-circle" size="sm" style={{ marginLeft: '10px' }} />
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const InlineFormLabel = FormLabel;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
|
|||||||
import uniqueId from 'lodash/uniqueId';
|
import uniqueId from 'lodash/uniqueId';
|
||||||
import { Tooltip } from '../../../Tooltip/Tooltip';
|
import { Tooltip } from '../../../Tooltip/Tooltip';
|
||||||
import * as PopperJS from 'popper.js';
|
import * as PopperJS from 'popper.js';
|
||||||
|
import { Icon } from '../../..';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -54,7 +55,7 @@ export class Switch extends PureComponent<Props, State> {
|
|||||||
{tooltip && (
|
{tooltip && (
|
||||||
<Tooltip placement={tooltipPlacement ? tooltipPlacement : 'auto'} content={tooltip} theme={'info'}>
|
<Tooltip placement={tooltipPlacement ? tooltipPlacement : 'auto'} content={tooltip} theme={'info'}>
|
||||||
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
||||||
<i className="fa fa-info-circle" />
|
<Icon name="info-circle" size="sm" style={{ marginLeft: '10px' }} />
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import React, { memo, useCallback } from 'react';
|
||||||
|
import { MatcherUIProps, FieldMatcherUIRegistryItem } from './types';
|
||||||
|
import { FieldMatcherID, fieldMatchers } from '@grafana/data';
|
||||||
|
import { Input } from '../Input/Input';
|
||||||
|
|
||||||
|
export const FieldNameByRegexMatcherEditor = memo<MatcherUIProps<string>>(props => {
|
||||||
|
const { options } = props;
|
||||||
|
|
||||||
|
const onBlur = useCallback(
|
||||||
|
(e: React.FocusEvent<HTMLInputElement>) => {
|
||||||
|
return props.onChange(e.target.value);
|
||||||
|
},
|
||||||
|
[props.onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return <Input placeholder="Enter regular expression" defaultValue={options} onBlur={onBlur} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fieldNameByRegexMatcherItem: FieldMatcherUIRegistryItem<string> = {
|
||||||
|
id: FieldMatcherID.byRegexp,
|
||||||
|
component: FieldNameByRegexMatcherEditor,
|
||||||
|
matcher: fieldMatchers.get(FieldMatcherID.byRegexp),
|
||||||
|
name: 'Filter by field using regex',
|
||||||
|
description: 'Set properties for fields with names matching provided regex',
|
||||||
|
};
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import React, { memo, useMemo, useCallback } from 'react';
|
||||||
|
import { MatcherUIProps, FieldMatcherUIRegistryItem } from './types';
|
||||||
|
import { FieldMatcherID, fieldMatchers, SelectableValue, FieldType, DataFrame } from '@grafana/data';
|
||||||
|
import { Select } from '../Select/Select';
|
||||||
|
|
||||||
|
export const FieldTypeMatcherEditor = memo<MatcherUIProps<string>>(props => {
|
||||||
|
const { data, options } = props;
|
||||||
|
const counts = useFieldCounts(data);
|
||||||
|
const selectOptions = useSelectOptions(counts, options);
|
||||||
|
|
||||||
|
const onChange = useCallback(
|
||||||
|
(selection: SelectableValue<string>) => {
|
||||||
|
return props.onChange(selection.value!);
|
||||||
|
},
|
||||||
|
[counts, props.onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedOption = selectOptions.find(v => v.value === options);
|
||||||
|
return <Select value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
const allTypes: Array<SelectableValue<FieldType>> = [
|
||||||
|
{ value: FieldType.number, label: 'Numeric' },
|
||||||
|
{ value: FieldType.string, label: 'String' },
|
||||||
|
{ value: FieldType.time, label: 'Time' },
|
||||||
|
{ value: FieldType.boolean, label: 'Boolean' },
|
||||||
|
{ value: FieldType.trace, label: 'Traces' },
|
||||||
|
{ value: FieldType.other, label: 'Other' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const useFieldCounts = (data: DataFrame[]): Map<FieldType, number> => {
|
||||||
|
return useMemo(() => {
|
||||||
|
const counts: Map<FieldType, number> = new Map();
|
||||||
|
for (const t of allTypes) {
|
||||||
|
counts.set(t.value!, 0);
|
||||||
|
}
|
||||||
|
for (const frame of data) {
|
||||||
|
for (const field of frame.fields) {
|
||||||
|
const key = field.type || FieldType.other;
|
||||||
|
let v = counts.get(key);
|
||||||
|
if (!v) {
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
counts.set(key, v + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counts;
|
||||||
|
}, [data]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSelectOptions = (counts: Map<string, number>, opt?: string): Array<SelectableValue<string>> => {
|
||||||
|
return useMemo(() => {
|
||||||
|
let found = false;
|
||||||
|
const options: Array<SelectableValue<string>> = [];
|
||||||
|
for (const t of allTypes) {
|
||||||
|
const count = counts.get(t.value!);
|
||||||
|
const match = opt === t.value;
|
||||||
|
if (count || match) {
|
||||||
|
options.push({
|
||||||
|
...t,
|
||||||
|
label: `${t.label} (${counts.get(t.value!)})`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt && !found) {
|
||||||
|
options.push({
|
||||||
|
value: opt,
|
||||||
|
label: `${opt} (No matches)`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}, [counts, opt]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fieldTypeMatcherItem: FieldMatcherUIRegistryItem<string> = {
|
||||||
|
id: FieldMatcherID.byType,
|
||||||
|
component: FieldTypeMatcherEditor,
|
||||||
|
matcher: fieldMatchers.get(FieldMatcherID.byType),
|
||||||
|
name: 'Filter by type',
|
||||||
|
description: 'Set properties for fields matching a type',
|
||||||
|
};
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Registry } from '@grafana/data';
|
import { Registry } from '@grafana/data';
|
||||||
import { fieldNameMatcherItem } from './FieldNameMatcherEditor';
|
import { fieldNameMatcherItem } from './FieldNameMatcherEditor';
|
||||||
import { FieldMatcherUIRegistryItem } from './types';
|
import { FieldMatcherUIRegistryItem } from './types';
|
||||||
|
import { fieldNameByRegexMatcherItem } from './FieldNameByRegexMatcherEditor';
|
||||||
|
import { fieldTypeMatcherItem } from './FieldTypeMatcherEditor';
|
||||||
|
|
||||||
export const fieldMatchersUI = new Registry<FieldMatcherUIRegistryItem<any>>(() => {
|
export const fieldMatchersUI = new Registry<FieldMatcherUIRegistryItem<any>>(() => {
|
||||||
return [fieldNameMatcherItem];
|
return [fieldNameMatcherItem, fieldNameByRegexMatcherItem, fieldTypeMatcherItem];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { findInsertIndex } from './suggestions';
|
||||||
|
|
||||||
|
describe('Check suggestion index', () => {
|
||||||
|
it('find last $ sign', () => {
|
||||||
|
const line = ' hello $123';
|
||||||
|
const { index, prefix } = findInsertIndex(line);
|
||||||
|
expect(index).toEqual(line.indexOf('$'));
|
||||||
|
expect(prefix).toEqual('$123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('insert into empty line', () => {
|
||||||
|
const line = '';
|
||||||
|
const { index, prefix } = findInsertIndex(line);
|
||||||
|
expect(index).toEqual(0);
|
||||||
|
expect(prefix).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('insert new word', () => {
|
||||||
|
const line = 'this is a new ';
|
||||||
|
const { index, prefix } = findInsertIndex(line);
|
||||||
|
expect(index).toEqual(line.length);
|
||||||
|
expect(prefix).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('complte a simple word', () => {
|
||||||
|
const line = 'SELECT * FROM tab';
|
||||||
|
const { index, prefix } = findInsertIndex(line);
|
||||||
|
expect(index).toEqual(line.lastIndexOf(' ') + 1);
|
||||||
|
expect(prefix).toEqual('tab');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('complete a quoted word', () => {
|
||||||
|
const line = 'SELECT "hello", "wo';
|
||||||
|
const { index, prefix } = findInsertIndex(line);
|
||||||
|
expect(index).toEqual(line.lastIndexOf('"') + 1);
|
||||||
|
expect(prefix).toEqual('wo');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,6 +2,33 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|||||||
|
|
||||||
import { CodeEditorSuggestionItem, CodeEditorSuggestionItemKind, CodeEditorSuggestionProvider } from './types';
|
import { CodeEditorSuggestionItem, CodeEditorSuggestionItemKind, CodeEditorSuggestionProvider } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal -- only exported for tests
|
||||||
|
*/
|
||||||
|
export function findInsertIndex(line: string): { index: number; prefix: string } {
|
||||||
|
for (let i = line.length - 1; i > 0; i--) {
|
||||||
|
const ch = line.charAt(i);
|
||||||
|
if (ch === '$') {
|
||||||
|
return {
|
||||||
|
index: i,
|
||||||
|
prefix: line.substring(i),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep these seperators
|
||||||
|
if (ch === ' ' || ch === '\t' || ch === '"' || ch === "'") {
|
||||||
|
return {
|
||||||
|
index: i + 1,
|
||||||
|
prefix: line.substring(i + 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
index: 0,
|
||||||
|
prefix: line,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getCompletionItems(
|
function getCompletionItems(
|
||||||
prefix: string,
|
prefix: string,
|
||||||
suggestions: CodeEditorSuggestionItem[],
|
suggestions: CodeEditorSuggestionItem[],
|
||||||
@@ -53,51 +80,39 @@ export function registerSuggestions(
|
|||||||
triggerCharacters: ['$'],
|
triggerCharacters: ['$'],
|
||||||
|
|
||||||
provideCompletionItems: (model, position, context) => {
|
provideCompletionItems: (model, position, context) => {
|
||||||
|
const range = {
|
||||||
|
startLineNumber: position.lineNumber,
|
||||||
|
endLineNumber: position.lineNumber,
|
||||||
|
startColumn: position.column,
|
||||||
|
endColumn: position.column,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple check if this was triggered by pressing `$`
|
||||||
if (context.triggerCharacter === '$') {
|
if (context.triggerCharacter === '$') {
|
||||||
const range = {
|
range.startColumn = position.column - 1;
|
||||||
startLineNumber: position.lineNumber,
|
|
||||||
endLineNumber: position.lineNumber,
|
|
||||||
startColumn: position.column - 1,
|
|
||||||
endColumn: position.column,
|
|
||||||
};
|
|
||||||
return {
|
return {
|
||||||
suggestions: getCompletionItems('$', getSuggestions(), range),
|
suggestions: getCompletionItems('$', getSuggestions(), range),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// find out if we are completing a property in the 'dependencies' object.
|
// Find the replacement region
|
||||||
const lineText = model.getValueInRange({
|
const currentLine = model.getValueInRange({
|
||||||
startLineNumber: position.lineNumber,
|
startLineNumber: position.lineNumber,
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
endLineNumber: position.lineNumber,
|
endLineNumber: position.lineNumber,
|
||||||
endColumn: position.column,
|
endColumn: position.column,
|
||||||
});
|
});
|
||||||
|
|
||||||
const idx = lineText.lastIndexOf('$');
|
const { index, prefix } = findInsertIndex(currentLine);
|
||||||
if (idx >= 0) {
|
range.startColumn = index + 1;
|
||||||
const range = {
|
|
||||||
startLineNumber: position.lineNumber,
|
const suggestions = getCompletionItems(prefix, getSuggestions(), range);
|
||||||
endLineNumber: position.lineNumber,
|
if (suggestions.length) {
|
||||||
startColumn: idx, // the last $ we found
|
// NOTE, this will replace any language provided suggestions
|
||||||
endColumn: position.column,
|
return { suggestions };
|
||||||
};
|
|
||||||
return {
|
|
||||||
suggestions: getCompletionItems(lineText.substr(idx), getSuggestions(), range),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty line that asked for suggestion
|
// Default language suggestions
|
||||||
if (lineText.trim().length < 1) {
|
|
||||||
return {
|
|
||||||
suggestions: getCompletionItems('', getSuggestions(), {
|
|
||||||
startLineNumber: position.lineNumber,
|
|
||||||
endLineNumber: position.lineNumber,
|
|
||||||
startColumn: position.column,
|
|
||||||
endColumn: position.column,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// console.log('complete?', lineText, context);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { TableCellProps } from './types';
|
import { isString } from 'lodash';
|
||||||
import { Tooltip } from '../Tooltip/Tooltip';
|
import { Tooltip } from '../Tooltip/Tooltip';
|
||||||
import { JSONFormatter } from '../JSONFormatter/JSONFormatter';
|
import { JSONFormatter } from '../JSONFormatter/JSONFormatter';
|
||||||
|
import { useStyles } from '../../themes';
|
||||||
|
import { TableCellProps } from './types';
|
||||||
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
|
|
||||||
export const JSONViewCell: FC<TableCellProps> = props => {
|
export const JSONViewCell: FC<TableCellProps> = props => {
|
||||||
const { field, cell, tableStyles } = props;
|
const { field, cell, tableStyles } = props;
|
||||||
@@ -16,11 +19,19 @@ export const JSONViewCell: FC<TableCellProps> = props => {
|
|||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const displayValue = JSON.stringify(cell.value);
|
let value = cell.value;
|
||||||
const content = <JSONTooltip value={cell.value} />;
|
let displayValue = value;
|
||||||
|
if (isString(value)) {
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch {} // ignore errors
|
||||||
|
} else {
|
||||||
|
displayValue = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
const content = <JSONTooltip value={value} />;
|
||||||
return (
|
return (
|
||||||
<div className={cx(txt, tableStyles.tableCell)}>
|
<div className={cx(txt, tableStyles.tableCell)}>
|
||||||
<Tooltip placement="auto" content={content} theme={'info'}>
|
<Tooltip placement="auto" content={content} theme="info-alt">
|
||||||
<div className={tableStyles.overflow}>{displayValue}</div>
|
<div className={tableStyles.overflow}>{displayValue}</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,12 +43,19 @@ interface PopupProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const JSONTooltip: FC<PopupProps> = props => {
|
const JSONTooltip: FC<PopupProps> = props => {
|
||||||
const clazz = css`
|
const styles = useStyles((theme: GrafanaTheme) => {
|
||||||
padding: 10px;
|
return {
|
||||||
`;
|
container: css`
|
||||||
|
padding: ${theme.spacing.xs};
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clazz}>
|
<div className={styles.container}>
|
||||||
<JSONFormatter json={props.value} open={4} />
|
<div>
|
||||||
|
<JSONFormatter json={props.value} open={4} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
z-index: 1071;
|
color: ${theme.colors.textWeak};
|
||||||
`,
|
`,
|
||||||
picker: css`
|
picker: css`
|
||||||
.rc-time-picker-panel-select {
|
.rc-time-picker-panel-select {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Story, Preview, Props } from '@storybook/addon-docs/blocks';
|
||||||
|
import { TimeRangeInput } from './TimeRangeInput';
|
||||||
|
|
||||||
|
# TimeRangeInput
|
||||||
|
|
||||||
|
A variant of `TimeRangePicker` for use in forms.
|
||||||
|
|
||||||
|
`dateTime(null)` can be used to provide empty time range value. The shape of the return value on input clear is:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
from: dateTime(null),
|
||||||
|
to: dateTime(null),
|
||||||
|
raw: {
|
||||||
|
from: dateTime(null),
|
||||||
|
to: dateTime(null),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`dateMath.isValid()` from `@grafana/data` can be used to check for a valid time range value.
|
||||||
|
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { TimeRangeInput } from '@grafana/ui';
|
||||||
|
|
||||||
|
<TimeRangeInput
|
||||||
|
value={timeRange}
|
||||||
|
onChange={range => console.log('range', range)}
|
||||||
|
onChangeTimeZone={tz => console.log('timezone', tz)}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
<Props of={TimeRangeInput} />
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { dateTime, TimeFragment } from '@grafana/data';
|
||||||
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||||
|
import { UseState } from '../../utils/storybook/UseState';
|
||||||
|
import { TimeRangeInput } from './TimeRangeInput';
|
||||||
|
import mdx from './TimeRangeInput.mdx';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Pickers and Editors/TimePickers/TimeRangeInput',
|
||||||
|
component: TimeRangeInput,
|
||||||
|
decorators: [withCenteredStory],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: mdx,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const basic = () => {
|
||||||
|
return (
|
||||||
|
<UseState
|
||||||
|
initialState={{
|
||||||
|
from: dateTime(),
|
||||||
|
to: dateTime(),
|
||||||
|
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(value, updateValue) => {
|
||||||
|
return (
|
||||||
|
<TimeRangeInput
|
||||||
|
value={value}
|
||||||
|
onChange={timeRange => {
|
||||||
|
action('onChange fired')(timeRange);
|
||||||
|
updateValue(timeRange);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</UseState>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearable = () => {
|
||||||
|
return (
|
||||||
|
<UseState
|
||||||
|
initialState={{
|
||||||
|
from: dateTime(),
|
||||||
|
to: dateTime(),
|
||||||
|
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(value, updateValue) => {
|
||||||
|
return (
|
||||||
|
<TimeRangeInput
|
||||||
|
clearable
|
||||||
|
value={value}
|
||||||
|
onChange={timeRange => {
|
||||||
|
action('onChange fired')(timeRange);
|
||||||
|
updateValue(timeRange);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</UseState>
|
||||||
|
);
|
||||||
|
};
|
||||||
144
packages/grafana-ui/src/components/TimePicker/TimeRangeInput.tsx
Normal file
144
packages/grafana-ui/src/components/TimePicker/TimeRangeInput.tsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import React, { FC, FormEvent, MouseEvent, useState } from 'react';
|
||||||
|
import { css, cx } from 'emotion';
|
||||||
|
import { dateTime, GrafanaTheme, TimeRange, TimeZone, dateMath } from '@grafana/data';
|
||||||
|
import { useStyles } from '../../themes/ThemeContext';
|
||||||
|
import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper';
|
||||||
|
import { Icon } from '../Icon/Icon';
|
||||||
|
import { getInputStyles } from '../Input/Input';
|
||||||
|
import { getFocusStyle } from '../Forms/commonStyles';
|
||||||
|
import { TimePickerButtonLabel } from './TimeRangePicker';
|
||||||
|
import { TimePickerContent } from './TimeRangePicker/TimePickerContent';
|
||||||
|
import { otherOptions, quickOptions } from './rangeOptions';
|
||||||
|
|
||||||
|
export const defaultTimeRange: TimeRange = {
|
||||||
|
from: dateTime().subtract(6, 'hour'),
|
||||||
|
to: dateTime(),
|
||||||
|
raw: { from: 'now-6h', to: 'now' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidTimeRange = (range: any) => {
|
||||||
|
return dateMath.isValid(range.from) && dateMath.isValid(range.to);
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
value: TimeRange;
|
||||||
|
timeZone?: TimeZone;
|
||||||
|
onChange: (timeRange: TimeRange) => void;
|
||||||
|
onChangeTimeZone?: (timeZone: TimeZone) => void;
|
||||||
|
hideTimeZone?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
clearable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
export const TimeRangeInput: FC<Props> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onChangeTimeZone,
|
||||||
|
clearable,
|
||||||
|
hideTimeZone = true,
|
||||||
|
timeZone = 'browser',
|
||||||
|
placeholder = 'Select time range',
|
||||||
|
}) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const styles = useStyles(getStyles);
|
||||||
|
|
||||||
|
const onOpen = (event: FormEvent<HTMLDivElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRangeChange = (timeRange: TimeRange) => {
|
||||||
|
onClose();
|
||||||
|
onChange(timeRange);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRangeClear = (event: MouseEvent<HTMLDivElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const from = dateTime(null);
|
||||||
|
const to = dateTime(null);
|
||||||
|
onChange({ from, to, raw: { from, to } });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div tabIndex={0} className={styles.pickerInput} aria-label="TimePicker Open Button" onClick={onOpen}>
|
||||||
|
{isValidTimeRange(value) ? (
|
||||||
|
<TimePickerButtonLabel value={value as TimeRange} timeZone={timeZone} />
|
||||||
|
) : (
|
||||||
|
<span className={styles.placeholder}>{placeholder}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span className={styles.caretIcon}>
|
||||||
|
{isValidTimeRange(value) && clearable && (
|
||||||
|
<Icon className={styles.clearIcon} name="times" size="lg" onClick={onRangeClear} />
|
||||||
|
)}
|
||||||
|
<Icon name={isOpen ? 'angle-up' : 'angle-down'} size="lg" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{isOpen && (
|
||||||
|
<ClickOutsideWrapper includeButtonPress={false} onClick={onClose}>
|
||||||
|
<TimePickerContent
|
||||||
|
timeZone={timeZone}
|
||||||
|
value={isValidTimeRange(value) ? (value as TimeRange) : defaultTimeRange}
|
||||||
|
onChange={onRangeChange}
|
||||||
|
otherOptions={otherOptions}
|
||||||
|
quickOptions={quickOptions}
|
||||||
|
onChangeTimeZone={onChangeTimeZone || noop}
|
||||||
|
className={styles.content}
|
||||||
|
hideTimeZone={hideTimeZone}
|
||||||
|
isReversed
|
||||||
|
/>
|
||||||
|
</ClickOutsideWrapper>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme) => {
|
||||||
|
const inputStyles = getInputStyles({ theme, invalid: false });
|
||||||
|
return {
|
||||||
|
container: css`
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
`,
|
||||||
|
content: css`
|
||||||
|
margin-left: 0;
|
||||||
|
`,
|
||||||
|
pickerInput: cx(
|
||||||
|
inputStyles.input,
|
||||||
|
inputStyles.wrapper,
|
||||||
|
css`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
cursor: pointer;
|
||||||
|
padding-right: 0;
|
||||||
|
${getFocusStyle(theme)};
|
||||||
|
`
|
||||||
|
),
|
||||||
|
caretIcon: cx(
|
||||||
|
inputStyles.suffix,
|
||||||
|
css`
|
||||||
|
position: relative;
|
||||||
|
margin-left: ${theme.spacing.xs};
|
||||||
|
`
|
||||||
|
),
|
||||||
|
clearIcon: css`
|
||||||
|
margin-right: ${theme.spacing.xs};
|
||||||
|
&:hover {
|
||||||
|
color: ${theme.colors.linkHover};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
placeholder: css`
|
||||||
|
color: ${theme.colors.formInputPlaceholderText};
|
||||||
|
opacity: 1;
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -14,44 +14,9 @@ import { withTheme, useTheme } from '../../themes/ThemeContext';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { isDateTime, rangeUtil, GrafanaTheme, dateTimeFormat, timeZoneFormatUserFriendly } from '@grafana/data';
|
import { isDateTime, rangeUtil, GrafanaTheme, dateTimeFormat, timeZoneFormatUserFriendly } from '@grafana/data';
|
||||||
import { TimeRange, TimeOption, TimeZone, dateMath } from '@grafana/data';
|
import { TimeRange, TimeZone, dateMath } from '@grafana/data';
|
||||||
import { Themeable } from '../../types';
|
import { Themeable } from '../../types';
|
||||||
|
import { otherOptions, quickOptions } from './rangeOptions';
|
||||||
const quickOptions: TimeOption[] = [
|
|
||||||
{ from: 'now-5m', to: 'now', display: 'Last 5 minutes', section: 3 },
|
|
||||||
{ from: 'now-15m', to: 'now', display: 'Last 15 minutes', section: 3 },
|
|
||||||
{ from: 'now-30m', to: 'now', display: 'Last 30 minutes', section: 3 },
|
|
||||||
{ from: 'now-1h', to: 'now', display: 'Last 1 hour', section: 3 },
|
|
||||||
{ from: 'now-3h', to: 'now', display: 'Last 3 hours', section: 3 },
|
|
||||||
{ from: 'now-6h', to: 'now', display: 'Last 6 hours', section: 3 },
|
|
||||||
{ from: 'now-12h', to: 'now', display: 'Last 12 hours', section: 3 },
|
|
||||||
{ from: 'now-24h', to: 'now', display: 'Last 24 hours', section: 3 },
|
|
||||||
{ from: 'now-2d', to: 'now', display: 'Last 2 days', section: 3 },
|
|
||||||
{ from: 'now-7d', to: 'now', display: 'Last 7 days', section: 3 },
|
|
||||||
{ from: 'now-30d', to: 'now', display: 'Last 30 days', section: 3 },
|
|
||||||
{ from: 'now-90d', to: 'now', display: 'Last 90 days', section: 3 },
|
|
||||||
{ from: 'now-6M', to: 'now', display: 'Last 6 months', section: 3 },
|
|
||||||
{ from: 'now-1y', to: 'now', display: 'Last 1 year', section: 3 },
|
|
||||||
{ from: 'now-2y', to: 'now', display: 'Last 2 years', section: 3 },
|
|
||||||
{ from: 'now-5y', to: 'now', display: 'Last 5 years', section: 3 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const otherOptions: TimeOption[] = [
|
|
||||||
{ from: 'now-1d/d', to: 'now-1d/d', display: 'Yesterday', section: 3 },
|
|
||||||
{ from: 'now-2d/d', to: 'now-2d/d', display: 'Day before yesterday', section: 3 },
|
|
||||||
{ from: 'now-7d/d', to: 'now-7d/d', display: 'This day last week', section: 3 },
|
|
||||||
{ from: 'now-1w/w', to: 'now-1w/w', display: 'Previous week', section: 3 },
|
|
||||||
{ from: 'now-1M/M', to: 'now-1M/M', display: 'Previous month', section: 3 },
|
|
||||||
{ from: 'now-1y/y', to: 'now-1y/y', display: 'Previous year', section: 3 },
|
|
||||||
{ from: 'now/d', to: 'now/d', display: 'Today', section: 3 },
|
|
||||||
{ from: 'now/d', to: 'now', display: 'Today so far', section: 3 },
|
|
||||||
{ from: 'now/w', to: 'now/w', display: 'This week', section: 3 },
|
|
||||||
{ from: 'now/w', to: 'now', display: 'This week so far', section: 3 },
|
|
||||||
{ from: 'now/M', to: 'now/M', display: 'This month', section: 3 },
|
|
||||||
{ from: 'now/M', to: 'now', display: 'This month so far', section: 3 },
|
|
||||||
{ from: 'now/y', to: 'now/y', display: 'This year', section: 3 },
|
|
||||||
{ from: 'now/y', to: 'now', display: 'This year so far', section: 3 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||||
return {
|
return {
|
||||||
@@ -122,6 +87,7 @@ export class UnthemedTimeRangePicker extends PureComponent<Props, State> {
|
|||||||
onOpen = (event: FormEvent<HTMLButtonElement>) => {
|
onOpen = (event: FormEvent<HTMLButtonElement>) => {
|
||||||
const { isOpen } = this.state;
|
const { isOpen } = this.state;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
this.setState({ isOpen: !isOpen });
|
this.setState({ isOpen: !isOpen });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,6 +144,7 @@ export class UnthemedTimeRangePicker extends PureComponent<Props, State> {
|
|||||||
otherOptions={otherOptions}
|
otherOptions={otherOptions}
|
||||||
quickOptions={quickOptions}
|
quickOptions={quickOptions}
|
||||||
history={history}
|
history={history}
|
||||||
|
showHistory
|
||||||
onChangeTimeZone={onChangeTimeZone}
|
onChangeTimeZone={onChangeTimeZone}
|
||||||
/>
|
/>
|
||||||
</ClickOutsideWrapper>
|
</ClickOutsideWrapper>
|
||||||
@@ -225,7 +192,9 @@ const TimePickerTooltip = ({ timeRange, timeZone }: { timeRange: TimeRange; time
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TimePickerButtonLabel = memo<Props>(({ hideText, value, timeZone }) => {
|
type LabelProps = Pick<Props, 'hideText' | 'value' | 'timeZone'>;
|
||||||
|
|
||||||
|
export const TimePickerButtonLabel = memo<LabelProps>(({ hideText, value, timeZone }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const styles = getLabelStyles(theme);
|
const styles = getLabelStyles(theme);
|
||||||
|
|
||||||
@@ -236,7 +205,7 @@ const TimePickerButtonLabel = memo<Props>(({ hideText, value, timeZone }) => {
|
|||||||
return (
|
return (
|
||||||
<span className={styles.container}>
|
<span className={styles.container}>
|
||||||
<span>{formattedRange(value, timeZone)}</span>
|
<span>{formattedRange(value, timeZone)}</span>
|
||||||
<span className={styles.utc}>{rangeUtil.describeTimeRangeAbbrevation(value, timeZone)}</span>
|
<span className={styles.utc}>{rangeUtil.describeTimeRangeAbbreviation(value, timeZone)}</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { dateTime } from '@grafana/data';
|
||||||
|
|
||||||
|
import { inputToValue } from './TimePickerCalendar';
|
||||||
|
|
||||||
|
describe('inputToValue', () => {
|
||||||
|
describe('when called with valid dates', () => {
|
||||||
|
describe('and from is after to', () => {
|
||||||
|
it('then to should be first in the result', () => {
|
||||||
|
const from = dateTime('2020-04-16T11:00:00.000Z');
|
||||||
|
const to = dateTime('2020-04-16T10:00:00.000Z');
|
||||||
|
|
||||||
|
const result = inputToValue(from, to);
|
||||||
|
|
||||||
|
expect(result).toEqual([new Date('2020-04-16T10:00:00.000Z'), new Date('2020-04-16T11:00:00.000Z')]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and from is before to', () => {
|
||||||
|
it('then to should be second in the result', () => {
|
||||||
|
const from = dateTime('2020-04-16T10:00:00.000Z');
|
||||||
|
const to = dateTime('2020-04-16T11:00:00.000Z');
|
||||||
|
|
||||||
|
const result = inputToValue(from, to);
|
||||||
|
|
||||||
|
expect(result).toEqual([new Date('2020-04-16T10:00:00.000Z'), new Date('2020-04-16T11:00:00.000Z')]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called with an invalid from datetime', () => {
|
||||||
|
it('then from should replaced with specified default', () => {
|
||||||
|
const from = dateTime('2020-04-32T10:00:00.000Z'); // invalid date
|
||||||
|
const to = dateTime('2020-04-16T10:00:00.000Z');
|
||||||
|
const invalidDateDefault = new Date('2020-04-16T11:00:00.000Z');
|
||||||
|
|
||||||
|
const result = inputToValue(from, to, invalidDateDefault);
|
||||||
|
|
||||||
|
expect(result).toEqual([new Date('2020-04-16T10:00:00.000Z'), new Date('2020-04-16T11:00:00.000Z')]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called with an invalid to datetime', () => {
|
||||||
|
it('then to should replaced with specified default', () => {
|
||||||
|
const from = dateTime('2020-04-16T10:00:00.000Z');
|
||||||
|
const to = dateTime('2020-04-32T10:00:00.000Z'); // invalid date
|
||||||
|
const invalidDateDefault = new Date('2020-04-16T11:00:00.000Z');
|
||||||
|
|
||||||
|
const result = inputToValue(from, to, invalidDateDefault);
|
||||||
|
|
||||||
|
expect(result).toEqual([new Date('2020-04-16T10:00:00.000Z'), new Date('2020-04-16T11:00:00.000Z')]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
import React, { memo, useState, useEffect, useCallback } from 'react';
|
import React, { FormEvent, memo, useCallback, useEffect, useState } from 'react';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
import Calendar from 'react-calendar/dist/entry.nostyle';
|
import Calendar from 'react-calendar/dist/entry.nostyle';
|
||||||
import { GrafanaTheme, DateTime, TimeZone, dateTimeParse } from '@grafana/data';
|
import { dateTime, DateTime, dateTimeParse, GrafanaTheme, TimeZone } from '@grafana/data';
|
||||||
import { useTheme, stylesFactory } from '../../../themes';
|
import { stylesFactory, useTheme } from '../../../themes';
|
||||||
import { TimePickerTitle } from './TimePickerTitle';
|
import { TimePickerTitle } from './TimePickerTitle';
|
||||||
import { Button } from '../../Button';
|
import { Button } from '../../Button';
|
||||||
import { Icon } from '../../Icon/Icon';
|
import { Icon } from '../../Icon/Icon';
|
||||||
import { Portal } from '../../Portal/Portal';
|
import { Portal } from '../../Portal/Portal';
|
||||||
import { ClickOutsideWrapper } from '../../ClickOutsideWrapper/ClickOutsideWrapper';
|
import { ClickOutsideWrapper } from '../../ClickOutsideWrapper/ClickOutsideWrapper';
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme, isReversed = false) => {
|
||||||
const containerBorder = theme.isDark ? theme.palette.dark9 : theme.palette.gray5;
|
const containerBorder = theme.isDark ? theme.palette.dark9 : theme.palette.gray5;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
container: css`
|
container: css`
|
||||||
top: -1px;
|
top: -1px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 544px;
|
${isReversed ? 'left' : 'right'}: 544px;
|
||||||
box-shadow: 0px 0px 20px ${theme.colors.dropdownShadow};
|
box-shadow: ${isReversed ? '10px' : '0px'} 0px 20px ${theme.colors.dropdownShadow};
|
||||||
background-color: ${theme.colors.bodyBg};
|
background-color: ${theme.colors.bodyBg};
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
border: 1px solid ${containerBorder};
|
border: 1px solid ${containerBorder};
|
||||||
@@ -28,7 +28,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
background-color: ${theme.colors.bodyBg};
|
background-color: ${theme.colors.bodyBg};
|
||||||
width: 19px;
|
width: 19px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
content: ' ';
|
content: ${!isReversed ? ' ' : ''};
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: -19px;
|
right: -19px;
|
||||||
@@ -189,17 +189,18 @@ interface Props {
|
|||||||
from: DateTime;
|
from: DateTime;
|
||||||
to: DateTime;
|
to: DateTime;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onApply: () => void;
|
onApply: (e: FormEvent<HTMLButtonElement>) => void;
|
||||||
onChange: (from: DateTime, to: DateTime) => void;
|
onChange: (from: DateTime, to: DateTime) => void;
|
||||||
isFullscreen: boolean;
|
isFullscreen: boolean;
|
||||||
timeZone?: TimeZone;
|
timeZone?: TimeZone;
|
||||||
|
isReversed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopPropagation = (event: React.MouseEvent<HTMLDivElement>) => event.stopPropagation();
|
const stopPropagation = (event: React.MouseEvent<HTMLDivElement>) => event.stopPropagation();
|
||||||
|
|
||||||
export const TimePickerCalendar = memo<Props>(props => {
|
export const TimePickerCalendar = memo<Props>(props => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme, props.isReversed);
|
||||||
const { isOpen, isFullscreen } = props;
|
const { isOpen, isFullscreen } = props;
|
||||||
|
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -284,14 +285,16 @@ const Footer = memo<Props>(({ onClose, onApply }) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function inputToValue(from: DateTime, to: DateTime): Date[] {
|
export function inputToValue(from: DateTime, to: DateTime, invalidDateDefault: Date = new Date()): Date[] {
|
||||||
const fromAsDate = from.toDate();
|
const fromAsDate = from.toDate();
|
||||||
const toAsDate = to.toDate();
|
const toAsDate = to.toDate();
|
||||||
|
const fromAsValidDate = dateTime(fromAsDate).isValid() ? fromAsDate : invalidDateDefault;
|
||||||
|
const toAsValidDate = dateTime(toAsDate).isValid() ? toAsDate : invalidDateDefault;
|
||||||
|
|
||||||
if (fromAsDate > toAsDate) {
|
if (fromAsValidDate > toAsValidDate) {
|
||||||
return [toAsDate, fromAsDate];
|
return [toAsValidDate, fromAsValidDate];
|
||||||
}
|
}
|
||||||
return [fromAsDate, toAsDate];
|
return [fromAsValidDate, toAsValidDate];
|
||||||
}
|
}
|
||||||
|
|
||||||
function useOnCalendarChange(onChange: (from: DateTime, to: DateTime) => void, timeZone?: TimeZone) {
|
function useOnCalendarChange(onChange: (from: DateTime, to: DateTime) => void, timeZone?: TimeZone) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GrafanaTheme, isDateTime, TimeOption, TimeRange, TimeZone } from '@grafana/data';
|
import { GrafanaTheme, isDateTime, TimeOption, TimeRange, TimeZone } from '@grafana/data';
|
||||||
import { css } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import React, { memo, useState } from 'react';
|
import React, { memo, useState } from 'react';
|
||||||
import { useMedia } from 'react-use';
|
import { useMedia } from 'react-use';
|
||||||
import { stylesFactory, useTheme } from '../../../themes';
|
import { stylesFactory, useTheme } from '../../../themes';
|
||||||
@@ -11,7 +11,7 @@ import { TimeRangeForm } from './TimeRangeForm';
|
|||||||
import { TimeRangeList } from './TimeRangeList';
|
import { TimeRangeList } from './TimeRangeList';
|
||||||
import { TimePickerFooter } from './TimePickerFooter';
|
import { TimePickerFooter } from './TimePickerFooter';
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme, isReversed) => {
|
||||||
const containerBorder = theme.isDark ? theme.palette.dark9 : theme.palette.gray5;
|
const containerBorder = theme.isDark ? theme.palette.dark9 : theme.palette.gray5;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -22,18 +22,12 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
z-index: ${theme.zIndex.modal};
|
z-index: ${theme.zIndex.modal};
|
||||||
width: 546px;
|
width: 546px;
|
||||||
top: 116%;
|
top: 116%;
|
||||||
margin-left: -322px;
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid ${containerBorder};
|
border: 1px solid ${containerBorder};
|
||||||
|
right: ${isReversed ? 'unset' : 0};
|
||||||
|
|
||||||
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
||||||
width: 218px;
|
width: 262px;
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: ${theme.breakpoints.sm}) {
|
|
||||||
width: 264px;
|
|
||||||
margin-left: -100px;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
body: css`
|
body: css`
|
||||||
@@ -43,9 +37,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
leftSide: css`
|
leftSide: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-right: 1px solid ${theme.colors.border1};
|
border-right: ${isReversed ? 'none' : `1px solid ${theme.colors.border1}`};
|
||||||
width: 60%;
|
width: 60%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
order: ${isReversed ? 1 : 0};
|
||||||
|
|
||||||
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -53,6 +48,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
`,
|
`,
|
||||||
rightSide: css`
|
rightSide: css`
|
||||||
width: 40% !important;
|
width: 40% !important;
|
||||||
|
border-right: ${isReversed ? `1px solid ${theme.colors.border1}` : 'none'};
|
||||||
|
|
||||||
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
@media only screen and (max-width: ${theme.breakpoints.lg}) {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
@@ -134,6 +130,11 @@ interface Props {
|
|||||||
quickOptions?: TimeOption[];
|
quickOptions?: TimeOption[];
|
||||||
otherOptions?: TimeOption[];
|
otherOptions?: TimeOption[];
|
||||||
history?: TimeRange[];
|
history?: TimeRange[];
|
||||||
|
showHistory?: boolean;
|
||||||
|
className?: string;
|
||||||
|
hideTimeZone?: boolean;
|
||||||
|
/** Reverse the order of relative and absolute range pickers. Used to left align the picker in forms */
|
||||||
|
isReversed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PropsWithScreenSize extends Props {
|
interface PropsWithScreenSize extends Props {
|
||||||
@@ -147,12 +148,12 @@ interface FormProps extends Omit<Props, 'history'> {
|
|||||||
|
|
||||||
export const TimePickerContentWithScreenSize: React.FC<PropsWithScreenSize> = props => {
|
export const TimePickerContentWithScreenSize: React.FC<PropsWithScreenSize> = props => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme, props.isReversed);
|
||||||
const historyOptions = mapToHistoryOptions(props.history, props.timeZone);
|
const historyOptions = mapToHistoryOptions(props.history, props.timeZone);
|
||||||
const { quickOptions = [], otherOptions = [], isFullscreen } = props;
|
const { quickOptions = [], otherOptions = [], isFullscreen } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={cx(styles.container, props.className)}>
|
||||||
<div className={styles.body}>
|
<div className={styles.body}>
|
||||||
<div className={styles.leftSide}>
|
<div className={styles.leftSide}>
|
||||||
<FullScreenForm {...props} visible={isFullscreen} historyOptions={historyOptions} />
|
<FullScreenForm {...props} visible={isFullscreen} historyOptions={historyOptions} />
|
||||||
@@ -176,7 +177,9 @@ export const TimePickerContentWithScreenSize: React.FC<PropsWithScreenSize> = pr
|
|||||||
/>
|
/>
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
</div>
|
</div>
|
||||||
{isFullscreen && <TimePickerFooter timeZone={props.timeZone} onChangeTimeZone={props.onChangeTimeZone} />}
|
{!props.hideTimeZone && isFullscreen && (
|
||||||
|
<TimePickerFooter timeZone={props.timeZone} onChangeTimeZone={props.onChangeTimeZone} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -218,14 +221,16 @@ const NarrowScreenForm: React.FC<FormProps> = props => {
|
|||||||
isFullscreen={false}
|
isFullscreen={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TimeRangeList
|
{props.showHistory && (
|
||||||
title="Recently used absolute ranges"
|
<TimeRangeList
|
||||||
options={props.historyOptions || []}
|
title="Recently used absolute ranges"
|
||||||
onSelect={props.onChange}
|
options={props.historyOptions || []}
|
||||||
value={props.value}
|
onSelect={props.onChange}
|
||||||
placeholderEmpty={null}
|
value={props.value}
|
||||||
timeZone={props.timeZone}
|
placeholderEmpty={null}
|
||||||
/>
|
timeZone={props.timeZone}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -246,18 +251,26 @@ const FullScreenForm: React.FC<FormProps> = props => {
|
|||||||
<div aria-label="TimePicker absolute time range" className={styles.title}>
|
<div aria-label="TimePicker absolute time range" className={styles.title}>
|
||||||
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
||||||
</div>
|
</div>
|
||||||
<TimeRangeForm value={props.value} timeZone={props.timeZone} onApply={props.onChange} isFullscreen={true} />
|
<TimeRangeForm
|
||||||
</div>
|
|
||||||
<div className={styles.recent}>
|
|
||||||
<TimeRangeList
|
|
||||||
title="Recently used absolute ranges"
|
|
||||||
options={props.historyOptions || []}
|
|
||||||
onSelect={props.onChange}
|
|
||||||
value={props.value}
|
value={props.value}
|
||||||
placeholderEmpty={<EmptyRecentList />}
|
|
||||||
timeZone={props.timeZone}
|
timeZone={props.timeZone}
|
||||||
|
onApply={props.onChange}
|
||||||
|
isFullscreen={true}
|
||||||
|
isReversed={props.isReversed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{props.showHistory && (
|
||||||
|
<div className={styles.recent}>
|
||||||
|
<TimeRangeList
|
||||||
|
title="Recently used absolute ranges"
|
||||||
|
options={props.historyOptions || []}
|
||||||
|
onSelect={props.onChange}
|
||||||
|
value={props.value}
|
||||||
|
placeholderEmpty={<EmptyRecentList />}
|
||||||
|
timeZone={props.timeZone}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { FormEvent, useState, useCallback } from 'react';
|
import React, { FormEvent, useState, useCallback, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
TimeZone,
|
TimeZone,
|
||||||
isDateTime,
|
isDateTime,
|
||||||
@@ -21,6 +21,7 @@ interface Props {
|
|||||||
onApply: (range: TimeRange) => void;
|
onApply: (range: TimeRange) => void;
|
||||||
timeZone?: TimeZone;
|
timeZone?: TimeZone;
|
||||||
roundup?: boolean;
|
roundup?: boolean;
|
||||||
|
isReversed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InputState {
|
interface InputState {
|
||||||
@@ -37,6 +38,12 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
|||||||
const [to, setTo] = useState<InputState>(valueToState(value.raw.to, true, timeZone));
|
const [to, setTo] = useState<InputState>(valueToState(value.raw.to, true, timeZone));
|
||||||
const [isOpen, setOpen] = useState(false);
|
const [isOpen, setOpen] = useState(false);
|
||||||
|
|
||||||
|
// Synchronize internal state with external value
|
||||||
|
useEffect(() => {
|
||||||
|
setFrom(valueToState(value.raw.from, false, timeZone));
|
||||||
|
setTo(valueToState(value.raw.to, true, timeZone));
|
||||||
|
}, [value.raw.from, value.raw.to, timeZone]);
|
||||||
|
|
||||||
const onOpen = useCallback(
|
const onOpen = useCallback(
|
||||||
(event: FormEvent<HTMLElement>) => {
|
(event: FormEvent<HTMLElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -55,16 +62,20 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
|||||||
[isFullscreen, onOpen]
|
[isFullscreen, onOpen]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onApply = useCallback(() => {
|
const onApply = useCallback(
|
||||||
if (to.invalid || from.invalid) {
|
(e: FormEvent<HTMLButtonElement>) => {
|
||||||
return;
|
e.preventDefault();
|
||||||
}
|
if (to.invalid || from.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const raw: RawTimeRange = { from: from.value, to: to.value };
|
const raw: RawTimeRange = { from: from.value, to: to.value };
|
||||||
const timeRange = rangeUtil.convertRawToRange(raw, timeZone);
|
const timeRange = rangeUtil.convertRawToRange(raw, timeZone);
|
||||||
|
|
||||||
props.onApply(timeRange);
|
props.onApply(timeRange);
|
||||||
}, [from, to, roundup, timeZone]);
|
},
|
||||||
|
[from, to, roundup, timeZone]
|
||||||
|
);
|
||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(from: DateTime, to: DateTime) => {
|
(from: DateTime, to: DateTime) => {
|
||||||
@@ -111,6 +122,7 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
|||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
|
isReversed={props.isReversed}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
exports[`TimePickerContent renders correctly in full screen 1`] = `
|
exports[`TimePickerContent renders correctly in full screen 1`] = `
|
||||||
<div
|
<div
|
||||||
className="css-1py9bjs"
|
className="css-ajr8sn"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="css-ooqtr4"
|
className="css-ooqtr4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="css-dlnzj7"
|
className="css-1f2wc71"
|
||||||
>
|
>
|
||||||
<FullScreenForm
|
<FullScreenForm
|
||||||
historyOptions={Array []}
|
historyOptions={Array []}
|
||||||
@@ -35,7 +35,7 @@ exports[`TimePickerContent renders correctly in full screen 1`] = `
|
|||||||
autoHide={false}
|
autoHide={false}
|
||||||
autoHideDuration={200}
|
autoHideDuration={200}
|
||||||
autoHideTimeout={200}
|
autoHideTimeout={200}
|
||||||
className="css-1o1b8dr"
|
className="css-10t714z"
|
||||||
hideTracksWhenNotNeeded={false}
|
hideTracksWhenNotNeeded={false}
|
||||||
setScrollTop={[Function]}
|
setScrollTop={[Function]}
|
||||||
>
|
>
|
||||||
@@ -103,13 +103,13 @@ exports[`TimePickerContent renders correctly in full screen 1`] = `
|
|||||||
|
|
||||||
exports[`TimePickerContent renders correctly in narrow screen 1`] = `
|
exports[`TimePickerContent renders correctly in narrow screen 1`] = `
|
||||||
<div
|
<div
|
||||||
className="css-1py9bjs"
|
className="css-ajr8sn"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="css-ooqtr4"
|
className="css-ooqtr4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="css-dlnzj7"
|
className="css-1f2wc71"
|
||||||
>
|
>
|
||||||
<FullScreenForm
|
<FullScreenForm
|
||||||
historyOptions={Array []}
|
historyOptions={Array []}
|
||||||
@@ -136,7 +136,7 @@ exports[`TimePickerContent renders correctly in narrow screen 1`] = `
|
|||||||
autoHide={false}
|
autoHide={false}
|
||||||
autoHideDuration={200}
|
autoHideDuration={200}
|
||||||
autoHideTimeout={200}
|
autoHideTimeout={200}
|
||||||
className="css-1o1b8dr"
|
className="css-10t714z"
|
||||||
hideTracksWhenNotNeeded={false}
|
hideTracksWhenNotNeeded={false}
|
||||||
setScrollTop={[Function]}
|
setScrollTop={[Function]}
|
||||||
>
|
>
|
||||||
@@ -200,13 +200,13 @@ exports[`TimePickerContent renders correctly in narrow screen 1`] = `
|
|||||||
|
|
||||||
exports[`TimePickerContent renders recent absolute ranges correctly 1`] = `
|
exports[`TimePickerContent renders recent absolute ranges correctly 1`] = `
|
||||||
<div
|
<div
|
||||||
className="css-1py9bjs"
|
className="css-ajr8sn"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="css-ooqtr4"
|
className="css-ooqtr4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="css-dlnzj7"
|
className="css-1f2wc71"
|
||||||
>
|
>
|
||||||
<FullScreenForm
|
<FullScreenForm
|
||||||
history={
|
history={
|
||||||
@@ -268,7 +268,7 @@ exports[`TimePickerContent renders recent absolute ranges correctly 1`] = `
|
|||||||
autoHide={false}
|
autoHide={false}
|
||||||
autoHideDuration={200}
|
autoHideDuration={200}
|
||||||
autoHideTimeout={200}
|
autoHideTimeout={200}
|
||||||
className="css-1o1b8dr"
|
className="css-10t714z"
|
||||||
hideTracksWhenNotNeeded={false}
|
hideTracksWhenNotNeeded={false}
|
||||||
setScrollTop={[Function]}
|
setScrollTop={[Function]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { TimeOption } from '@grafana/data';
|
||||||
|
|
||||||
|
export const quickOptions: TimeOption[] = [
|
||||||
|
{ from: 'now-5m', to: 'now', display: 'Last 5 minutes', section: 3 },
|
||||||
|
{ from: 'now-15m', to: 'now', display: 'Last 15 minutes', section: 3 },
|
||||||
|
{ from: 'now-30m', to: 'now', display: 'Last 30 minutes', section: 3 },
|
||||||
|
{ from: 'now-1h', to: 'now', display: 'Last 1 hour', section: 3 },
|
||||||
|
{ from: 'now-3h', to: 'now', display: 'Last 3 hours', section: 3 },
|
||||||
|
{ from: 'now-6h', to: 'now', display: 'Last 6 hours', section: 3 },
|
||||||
|
{ from: 'now-12h', to: 'now', display: 'Last 12 hours', section: 3 },
|
||||||
|
{ from: 'now-24h', to: 'now', display: 'Last 24 hours', section: 3 },
|
||||||
|
{ from: 'now-2d', to: 'now', display: 'Last 2 days', section: 3 },
|
||||||
|
{ from: 'now-7d', to: 'now', display: 'Last 7 days', section: 3 },
|
||||||
|
{ from: 'now-30d', to: 'now', display: 'Last 30 days', section: 3 },
|
||||||
|
{ from: 'now-90d', to: 'now', display: 'Last 90 days', section: 3 },
|
||||||
|
{ from: 'now-6M', to: 'now', display: 'Last 6 months', section: 3 },
|
||||||
|
{ from: 'now-1y', to: 'now', display: 'Last 1 year', section: 3 },
|
||||||
|
{ from: 'now-2y', to: 'now', display: 'Last 2 years', section: 3 },
|
||||||
|
{ from: 'now-5y', to: 'now', display: 'Last 5 years', section: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const otherOptions: TimeOption[] = [
|
||||||
|
{ from: 'now-1d/d', to: 'now-1d/d', display: 'Yesterday', section: 3 },
|
||||||
|
{ from: 'now-2d/d', to: 'now-2d/d', display: 'Day before yesterday', section: 3 },
|
||||||
|
{ from: 'now-7d/d', to: 'now-7d/d', display: 'This day last week', section: 3 },
|
||||||
|
{ from: 'now-1w/w', to: 'now-1w/w', display: 'Previous week', section: 3 },
|
||||||
|
{ from: 'now-1M/M', to: 'now-1M/M', display: 'Previous month', section: 3 },
|
||||||
|
{ from: 'now-1y/y', to: 'now-1y/y', display: 'Previous year', section: 3 },
|
||||||
|
{ from: 'now/d', to: 'now/d', display: 'Today', section: 3 },
|
||||||
|
{ from: 'now/d', to: 'now', display: 'Today so far', section: 3 },
|
||||||
|
{ from: 'now/w', to: 'now/w', display: 'This week', section: 3 },
|
||||||
|
{ from: 'now/w', to: 'now', display: 'This week so far', section: 3 },
|
||||||
|
{ from: 'now/M', to: 'now/M', display: 'This month', section: 3 },
|
||||||
|
{ from: 'now/M', to: 'now', display: 'This month so far', section: 3 },
|
||||||
|
{ from: 'now/y', to: 'now/y', display: 'This year', section: 3 },
|
||||||
|
{ from: 'now/y', to: 'now', display: 'This year so far', section: 3 },
|
||||||
|
];
|
||||||
22
packages/grafana-ui/src/components/Tooltip/Tooltip.mdx
Normal file
22
packages/grafana-ui/src/components/Tooltip/Tooltip.mdx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Props } from '@storybook/addon-docs/blocks';
|
||||||
|
import { Tooltip } from './Tooltip';
|
||||||
|
|
||||||
|
# Tooltip
|
||||||
|
|
||||||
|
## Theme
|
||||||
|
There are currently themes available for the Tooltip.
|
||||||
|
|
||||||
|
- Info
|
||||||
|
- Error
|
||||||
|
- Info-alt (alternative)
|
||||||
|
|
||||||
|
### Info
|
||||||
|
This is the default theme, usually used in forms to show more information.
|
||||||
|
|
||||||
|
### Error
|
||||||
|
Tooltip with a red background.
|
||||||
|
|
||||||
|
### Info alternative
|
||||||
|
We added this to be able to add a `<JSONFormatter />` in the tooltip.
|
||||||
|
|
||||||
|
<Props of={Tooltip } />
|
||||||
28
packages/grafana-ui/src/components/Tooltip/Tooltip.story.tsx
Normal file
28
packages/grafana-ui/src/components/Tooltip/Tooltip.story.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { select } from '@storybook/addon-knobs';
|
||||||
|
import { Tooltip } from './Tooltip';
|
||||||
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
import mdx from '../Tooltip/Tooltip.mdx';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Overlays/Tooltip',
|
||||||
|
component: Tooltip,
|
||||||
|
decorators: [withCenteredStory],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: mdx,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const basic = () => {
|
||||||
|
const VISUAL_GROUP = 'Visual options';
|
||||||
|
// ---
|
||||||
|
const theme = select('Theme', ['info', 'error', 'info-alt'], 'info', VISUAL_GROUP);
|
||||||
|
return (
|
||||||
|
<Tooltip content="This is a tooltip" theme={theme}>
|
||||||
|
<Button>Hover me for Tooltip </Button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { createRef } from 'react';
|
import React, { createRef, FC } from 'react';
|
||||||
import * as PopperJS from 'popper.js';
|
import * as PopperJS from 'popper.js';
|
||||||
import { Popover } from './Popover';
|
import { Popover } from './Popover';
|
||||||
import { PopoverController, UsingPopperProps } from './PopoverController';
|
import { PopoverController, UsingPopperProps } from './PopoverController';
|
||||||
|
|
||||||
export interface TooltipProps extends UsingPopperProps {
|
export interface TooltipProps extends UsingPopperProps {
|
||||||
theme?: 'info' | 'error';
|
theme?: 'info' | 'error' | 'info-alt';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PopoverContentProps {
|
export interface PopoverContentProps {
|
||||||
@@ -13,7 +13,7 @@ export interface PopoverContentProps {
|
|||||||
|
|
||||||
export type PopoverContent = string | React.ReactElement<any> | ((props: PopoverContentProps) => JSX.Element);
|
export type PopoverContent = string | React.ReactElement<any> | ((props: PopoverContentProps) => JSX.Element);
|
||||||
|
|
||||||
export const Tooltip = ({ children, theme, ...controllerProps }: TooltipProps) => {
|
export const Tooltip: FC<TooltipProps> = ({ children, theme, ...controllerProps }: TooltipProps) => {
|
||||||
const tooltipTriggerRef = createRef<PopperJS.ReferenceObject>();
|
const tooltipTriggerRef = createRef<PopperJS.ReferenceObject>();
|
||||||
const popperBackgroundClassName = 'popper__background' + (theme ? ' popper__background--' + theme : '');
|
const popperBackgroundClassName = 'popper__background' + (theme ? ' popper__background--' + theme : '');
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ $popper-margin-from-ref: 5px;
|
|||||||
&.popper__background--info {
|
&.popper__background--info {
|
||||||
@include popper-theme($popover-help-bg, $popover-help-color);
|
@include popper-theme($popover-help-bg, $popover-help-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.popper__background--info-alt {
|
||||||
|
@include popper-theme($popover-code-bg, $text-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.popper__arrow {
|
.popper__arrow {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ const setup = (spy?: any, propOverrides?: object) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
valueMappings: [
|
valueMappings: [
|
||||||
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
{ id: 1, type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
||||||
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
{ id: 2, type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,9 +35,7 @@ describe('On remove mapping', () => {
|
|||||||
const remove = wrapper.find('button[aria-label="ValueMappingsEditor remove button"]');
|
const remove = wrapper.find('button[aria-label="ValueMappingsEditor remove button"]');
|
||||||
remove.at(0).simulate('click');
|
remove.at(0).simulate('click');
|
||||||
|
|
||||||
expect(onChangeSpy).toBeCalledWith([
|
expect(onChangeSpy).toBeCalledWith([{ id: 2, type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' }]);
|
||||||
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove mapping at index 1', () => {
|
it('should remove mapping at index 1', () => {
|
||||||
@@ -47,9 +45,7 @@ describe('On remove mapping', () => {
|
|||||||
const remove = wrapper.find('button[aria-label="ValueMappingsEditor remove button"]');
|
const remove = wrapper.find('button[aria-label="ValueMappingsEditor remove button"]');
|
||||||
remove.at(1).simulate('click');
|
remove.at(1).simulate('click');
|
||||||
|
|
||||||
expect(onChangeSpy).toBeCalledWith([
|
expect(onChangeSpy).toBeCalledWith([{ id: 1, type: MappingType.ValueToText, value: '20', text: 'Ok' }]);
|
||||||
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -62,9 +58,9 @@ describe('Next id to add', () => {
|
|||||||
add.at(0).simulate('click');
|
add.at(0).simulate('click');
|
||||||
|
|
||||||
expect(onChangeSpy).toBeCalledWith([
|
expect(onChangeSpy).toBeCalledWith([
|
||||||
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
{ id: 1, type: MappingType.ValueToText, value: '20', text: 'Ok' },
|
||||||
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
{ id: 2, type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
|
||||||
{ id: 3, operator: '', type: MappingType.ValueToText, from: '', to: '', text: '' },
|
{ id: 3, type: MappingType.ValueToText, from: '', to: '', text: '' },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,8 +69,6 @@ describe('Next id to add', () => {
|
|||||||
const wrapper = setup(onChangeSpy, { valueMappings: [] });
|
const wrapper = setup(onChangeSpy, { valueMappings: [] });
|
||||||
const add = wrapper.find('*[aria-label="ValueMappingsEditor add mapping button"]');
|
const add = wrapper.find('*[aria-label="ValueMappingsEditor add mapping button"]');
|
||||||
add.at(0).simulate('click');
|
add.at(0).simulate('click');
|
||||||
expect(onChangeSpy).toBeCalledWith([
|
expect(onChangeSpy).toBeCalledWith([{ id: 0, type: MappingType.ValueToText, from: '', to: '', text: '' }]);
|
||||||
{ id: 0, operator: '', type: MappingType.ValueToText, from: '', to: '', text: '' },
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export const ValueMappingsEditor: React.FC<Props> = ({ valueMappings, onChange,
|
|||||||
type: MappingType.ValueToText,
|
type: MappingType.ValueToText,
|
||||||
from: '',
|
from: '',
|
||||||
to: '',
|
to: '',
|
||||||
operator: '',
|
|
||||||
text: '',
|
text: '',
|
||||||
};
|
};
|
||||||
const id = update && update.length > 0 ? Math.max(...update.map(v => v.id)) + 1 : 0;
|
const id = update && update.length > 0 ? Math.max(...update.map(v => v.id)) + 1 : 0;
|
||||||
|
|||||||
@@ -159,11 +159,12 @@ export { Checkbox } from './Forms/Checkbox';
|
|||||||
|
|
||||||
export { TextArea } from './TextArea/TextArea';
|
export { TextArea } from './TextArea/TextArea';
|
||||||
export { FileUpload } from './FileUpload/FileUpload';
|
export { FileUpload } from './FileUpload/FileUpload';
|
||||||
|
export { TimeRangeInput } from './TimePicker/TimeRangeInput';
|
||||||
|
|
||||||
// Legacy forms
|
// Legacy forms
|
||||||
|
|
||||||
// Export this until we've figured out a good approach to inline form styles.
|
// Export this until we've figured out a good approach to inline form styles.
|
||||||
export { FormLabel as InlineFormLabel } from './FormLabel/FormLabel';
|
export { InlineFormLabel } from './FormLabel/FormLabel';
|
||||||
|
|
||||||
// Select
|
// Select
|
||||||
import { Select, AsyncSelect } from './Forms/Legacy/Select/Select';
|
import { Select, AsyncSelect } from './Forms/Legacy/Select/Select';
|
||||||
|
|||||||
73
packages/grafana-ui/src/themes/ThemeContext.test.tsx
Normal file
73
packages/grafana-ui/src/themes/ThemeContext.test.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
|
import { renderHook } from '@testing-library/react-hooks';
|
||||||
|
import { css } from 'emotion';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
import { memoizedStyleCreators, mockThemeContext, useStyles } from './ThemeContext';
|
||||||
|
|
||||||
|
describe('useStyles', () => {
|
||||||
|
it('memoizes the passed in function correctly', () => {
|
||||||
|
const stylesCreator = () => ({});
|
||||||
|
const { rerender, result } = renderHook(() => useStyles(stylesCreator));
|
||||||
|
const storedReference = result.current;
|
||||||
|
|
||||||
|
rerender();
|
||||||
|
expect(storedReference).toBe(result.current);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not memoize if the passed in function changes every time', () => {
|
||||||
|
const { rerender, result } = renderHook(() => useStyles(() => ({})));
|
||||||
|
const storedReference = result.current;
|
||||||
|
rerender();
|
||||||
|
expect(storedReference).not.toBe(result.current);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates the memoized function when the theme changes', () => {
|
||||||
|
const stylesCreator = () => ({});
|
||||||
|
const { rerender, result } = renderHook(() => useStyles(stylesCreator));
|
||||||
|
const storedReference = result.current;
|
||||||
|
|
||||||
|
const restoreThemeContext = mockThemeContext({});
|
||||||
|
rerender();
|
||||||
|
expect(storedReference).not.toBe(result.current);
|
||||||
|
restoreThemeContext();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cleans up memoized functions whenever a new one comes along or the component unmounts', () => {
|
||||||
|
const styleCreators: Function[] = [];
|
||||||
|
const { rerender, unmount } = renderHook(() => {
|
||||||
|
const styleCreator = () => ({});
|
||||||
|
styleCreators.push(styleCreator);
|
||||||
|
return useStyles(styleCreator);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(typeof memoizedStyleCreators.get(styleCreators[0])).toBe('function');
|
||||||
|
rerender();
|
||||||
|
expect(memoizedStyleCreators.get(styleCreators[0])).toBeUndefined();
|
||||||
|
expect(typeof memoizedStyleCreators.get(styleCreators[1])).toBe('function');
|
||||||
|
unmount();
|
||||||
|
expect(memoizedStyleCreators.get(styleCreators[0])).toBeUndefined();
|
||||||
|
expect(memoizedStyleCreators.get(styleCreators[1])).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes in theme and returns style object', done => {
|
||||||
|
const Dummy: React.FC = function() {
|
||||||
|
const styles = useStyles(theme => {
|
||||||
|
expect(theme).toEqual(config.theme);
|
||||||
|
|
||||||
|
return {
|
||||||
|
someStyle: css`
|
||||||
|
color: ${theme.palette.critical};
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(typeof styles.someStyle).toBe('string');
|
||||||
|
done();
|
||||||
|
|
||||||
|
return <div>dummy</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
mount(<Dummy />);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import React, { useContext } from 'react';
|
|
||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
||||||
|
|
||||||
import { getTheme } from './getTheme';
|
|
||||||
import { Themeable } from '../types/theme';
|
|
||||||
import { GrafanaTheme, GrafanaThemeType } from '@grafana/data';
|
import { GrafanaTheme, GrafanaThemeType } from '@grafana/data';
|
||||||
|
import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||||
|
import React, { useContext, useEffect } from 'react';
|
||||||
|
import { Themeable } from '../types/theme';
|
||||||
|
import { getTheme } from './getTheme';
|
||||||
import { stylesFactory } from './stylesFactory';
|
import { stylesFactory } from './stylesFactory';
|
||||||
|
|
||||||
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||||
@@ -14,6 +13,9 @@ type Subtract<T, K> = Omit<T, keyof K>;
|
|||||||
*/
|
*/
|
||||||
let ThemeContextMock: React.Context<GrafanaTheme> | null = null;
|
let ThemeContextMock: React.Context<GrafanaTheme> | null = null;
|
||||||
|
|
||||||
|
// Used by useStyles()
|
||||||
|
export const memoizedStyleCreators = new WeakMap();
|
||||||
|
|
||||||
// Use Grafana Dark theme by default
|
// Use Grafana Dark theme by default
|
||||||
export const ThemeContext = React.createContext(getTheme(GrafanaThemeType.Dark));
|
export const ThemeContext = React.createContext(getTheme(GrafanaThemeType.Dark));
|
||||||
ThemeContext.displayName = 'ThemeContext';
|
ThemeContext.displayName = 'ThemeContext';
|
||||||
@@ -38,12 +40,31 @@ export const withTheme = <P extends Themeable, S extends {} = {}>(Component: Rea
|
|||||||
export function useTheme(): GrafanaTheme {
|
export function useTheme(): GrafanaTheme {
|
||||||
return useContext(ThemeContextMock || ThemeContext);
|
return useContext(ThemeContextMock || ThemeContext);
|
||||||
}
|
}
|
||||||
/** Hook for using memoized styles with access to the theme. */
|
|
||||||
export const useStyles = (getStyles: (theme?: GrafanaTheme) => any) => {
|
/**
|
||||||
const currentTheme = useTheme();
|
* Hook for using memoized styles with access to the theme.
|
||||||
const callback = stylesFactory(stylesTheme => getStyles(stylesTheme));
|
*
|
||||||
return callback(currentTheme);
|
* NOTE: For memoization to work, you need to ensure that the function
|
||||||
};
|
* you pass in doesn't change, or only if it needs to. (i.e. declare
|
||||||
|
* your style creator outside of a function component or use `useCallback()`.)
|
||||||
|
* */
|
||||||
|
export function useStyles<T>(getStyles: (theme: GrafanaTheme) => T) {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
let memoizedStyleCreator = memoizedStyleCreators.get(getStyles);
|
||||||
|
if (!memoizedStyleCreator) {
|
||||||
|
memoizedStyleCreator = stylesFactory(getStyles);
|
||||||
|
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
memoizedStyleCreators.delete(getStyles);
|
||||||
|
};
|
||||||
|
}, [getStyles]);
|
||||||
|
|
||||||
|
return memoizedStyleCreator(theme);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables theme context mocking
|
* Enables theme context mocking
|
||||||
|
|||||||
@@ -300,6 +300,9 @@ $popover-help-bg: $tooltipBackground;
|
|||||||
$popover-help-color: $text-color;
|
$popover-help-color: $text-color;
|
||||||
$popover-error-bg: $btn-danger-bg;
|
$popover-error-bg: $btn-danger-bg;
|
||||||
|
|
||||||
|
$popover-code-bg: $popover-bg;
|
||||||
|
$popover-code-boxshadow: $tooltipShadow;
|
||||||
|
|
||||||
// images
|
// images
|
||||||
$checkboxImageUrl: '../img/checkbox.png';
|
$checkboxImageUrl: '../img/checkbox.png';
|
||||||
|
|
||||||
|
|||||||
@@ -293,6 +293,9 @@ $popover-error-bg: $btn-danger-bg;
|
|||||||
$popover-help-bg: $tooltipBackground;
|
$popover-help-bg: $tooltipBackground;
|
||||||
$popover-help-color: $tooltipColor;
|
$popover-help-color: $tooltipColor;
|
||||||
|
|
||||||
|
$popover-code-bg: ${theme.colors.bg1};
|
||||||
|
$popover-code-boxshadow: 0 0 5px $gray60;
|
||||||
|
|
||||||
// images
|
// images
|
||||||
$checkboxImageUrl: '../img/checkbox_white.png';
|
$checkboxImageUrl: '../img/checkbox_white.png';
|
||||||
|
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ export const getStandardFieldConfigs = () => {
|
|||||||
id: 'mappings',
|
id: 'mappings',
|
||||||
path: 'mappings',
|
path: 'mappings',
|
||||||
name: 'Value mappings',
|
name: 'Value mappings',
|
||||||
|
description: 'Modify the display text based on input value',
|
||||||
|
|
||||||
editor: standardEditorsRegistry.get('mappings').editor as any,
|
editor: standardEditorsRegistry.get('mappings').editor as any,
|
||||||
override: standardEditorsRegistry.get('mappings').editor as any,
|
override: standardEditorsRegistry.get('mappings').editor as any,
|
||||||
|
|||||||
7
packages/grafana-ui/src/utils/storybook/useSize.ts
Normal file
7
packages/grafana-ui/src/utils/storybook/useSize.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { select } from '@storybook/addon-knobs';
|
||||||
|
import { ComponentSize } from '../../types/size';
|
||||||
|
|
||||||
|
export function useSize(size: ComponentSize = 'md') {
|
||||||
|
const sizes = ['xs', 'sm', 'md', 'lg'];
|
||||||
|
return select('Size', sizes, size);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@jaegertracing/jaeger-ui-components",
|
"name": "@jaegertracing/jaeger-ui-components",
|
||||||
"version": "7.1.0-pre.0",
|
"version": "7.1.5",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
"typescript": "3.9.3"
|
"typescript": "3.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grafana/data": "7.1.0-pre.0",
|
"@grafana/data": "7.1.5",
|
||||||
|
"@grafana/ui": "7.1.5",
|
||||||
"@types/classnames": "^2.2.7",
|
"@types/classnames": "^2.2.7",
|
||||||
"@types/deep-freeze": "^0.1.1",
|
"@types/deep-freeze": "^0.1.1",
|
||||||
"@types/hoist-non-react-statics": "^3.3.1",
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
"@types/moment": "^2.13.0",
|
"@types/moment": "^2.13.0",
|
||||||
"@types/react-icons": "2.2.7",
|
"@types/react-icons": "2.2.7",
|
||||||
"@types/recompose": "^0.30.7",
|
"@types/recompose": "^0.30.7",
|
||||||
|
"@types/slate-react": "0.22.5",
|
||||||
"chance": "^1.0.10",
|
"chance": "^1.0.10",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"combokeys": "^3.0.0",
|
"combokeys": "^3.0.0",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user