diff --git a/CHANGELOG.md b/CHANGELOG.md index 11b2af0..4d182e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.1.1 +- enhancement query editor ui +- add timezone label at query editor +- get metric bug fix +- add Metrics & Dimensions description +- add Metadata type for typescript +- remove unused function + + ## 0.1.0 Initial release. Not fit for production use. diff --git a/package.json b/package.json index 8cf97ab..257310a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blackcowmoo-googleanalytics-datasource", - "version": "0.1.0", + "version": "0.1.1", "description": " Google-Analytics to Grafana", "scripts": { "build": "rm -rf node_modules/@grafana/data/node_modules; grafana-toolkit plugin:build", diff --git a/pkg/models.go b/pkg/models.go index c33d674..9d09d92 100644 --- a/pkg/models.go +++ b/pkg/models.go @@ -130,6 +130,7 @@ type MetadataItemAttribute struct { Description string `json:"description,omitempty"` AllowedInSegments string `json:"allowedInSegments,omitempty"` AddedInAPIVersion string `json:"addedInApiVersion,omitempty"` + ReplacedBy string `json:"replacedBy,omitempty"` } type AttributeType string @@ -160,7 +161,7 @@ func (ga *GoogleAnalytics) getFilteredMetadata() ([]MetadataItem, []MetadataItem var dimensionItems = make([]MetadataItem, 0) var metricItems = make([]MetadataItem, 0) for _, item := range metadata.Items { - if item.Attributes.Status == "DEPRECATED" { + if item.Attributes.Status == "DEPRECATED" || item.Attributes.ReplacedBy != "" { continue } if item.Attributes.Type == AttributeTypeDimension { @@ -170,7 +171,7 @@ func (ga *GoogleAnalytics) getFilteredMetadata() ([]MetadataItem, []MetadataItem } } - return dimensionItems, metadata.Items, nil + return metricItems, dimensionItems, nil } func (ga *GoogleAnalytics) GetDimensions() ([]MetadataItem, error) { @@ -179,7 +180,7 @@ func (ga *GoogleAnalytics) GetDimensions() ([]MetadataItem, error) { return dimensions.([]MetadataItem), nil } - dimensions, _, err := ga.getFilteredMetadata() + _, dimensions, err := ga.getFilteredMetadata() if err != nil { return nil, err } @@ -194,7 +195,7 @@ func (ga *GoogleAnalytics) GetMetrics() ([]MetadataItem, error) { if metrics, _, found := ga.Cache.GetWithExpiration(cacheKey); found { return metrics.([]MetadataItem), nil } - _, metrics, err := ga.getFilteredMetadata() + metrics, _, err := ga.getFilteredMetadata() if err != nil { return nil, err } diff --git a/src/DataSource.ts b/src/DataSource.ts index bfee152..41c74bc 100644 --- a/src/DataSource.ts +++ b/src/DataSource.ts @@ -1,6 +1,6 @@ import { DataSourceInstanceSettings, SelectableValue } from '@grafana/data'; import { DataSourceWithBackend } from '@grafana/runtime'; -import { GADataSourceOptions, GAQuery } from './types'; +import { GADataSourceOptions, GAMetadata, GAQuery } from './types'; export class DataSource extends DataSourceWithBackend { constructor(instanceSettings: DataSourceInstanceSettings) { @@ -38,14 +38,17 @@ export class DataSource extends DataSourceWithBackend>> { + async getMetrics(query: string): Promise>> { return this.getResource('metrics').then(({ metrics }) => { - return metrics.reduce((pre: Array>, element: any) => { - let id = element.id as string; - if (query && id.toLowerCase().indexOf(query) > -1) { + return metrics.reduce((pre: Array>, element: GAMetadata) => { + if ( + element.id.toLowerCase().indexOf(query) > -1 || + element.attributes.uiName.toLowerCase().indexOf(query) > -1 + ) { pre.push({ - label: element.id, + label: element.attributes.uiName, value: element.id, + description: element.attributes.description, } as SelectableValue); } return pre; @@ -53,14 +56,17 @@ export class DataSource extends DataSourceWithBackend>> { + async getDimensions(query: string): Promise>> { return this.getResource('dimensions').then(({ dimensions }) => { - return dimensions.reduce((pre: Array>, element: any) => { - let id = element.id as string; - if (query && id.toLowerCase().indexOf(query) > -1) { + return dimensions.reduce((pre: Array>, element: GAMetadata) => { + if ( + element.id.toLowerCase().indexOf(query) > -1 || + element.attributes.uiName.toLowerCase().indexOf(query) > -1 + ) { pre.push({ - label: element.id, + label: element.attributes.uiName, value: element.id, + description: element.attributes.description, } as SelectableValue); } return pre; diff --git a/src/QueryEditor.tsx b/src/QueryEditor.tsx index 87b6df7..4629e6c 100644 --- a/src/QueryEditor.tsx +++ b/src/QueryEditor.tsx @@ -1,5 +1,5 @@ import { QueryEditorProps, SelectableValue } from '@grafana/data'; -import { AsyncMultiSelect, InlineFormLabel, SegmentAsync } from '@grafana/ui'; +import { AsyncMultiSelect, InlineFormLabel, InlineLabel, SegmentAsync } from '@grafana/ui'; import { DataSource } from 'DataSource'; import React, { PureComponent } from 'react'; import { GADataSourceOptions, GAQuery } from 'types'; @@ -8,16 +8,6 @@ type Props = QueryEditorProps; const defaultCacheDuration = 300; -export const formatCacheTimeLabel = (s: number = defaultCacheDuration) => { - if (s < 60) { - return s + 's'; - } else if (s < 3600) { - return s / 60 + 'm'; - } - - return s / 3600 + 'h'; -}; - export class QueryEditor extends PureComponent { constructor(props: Readonly) { super(props); @@ -121,126 +111,120 @@ export class QueryEditor extends PureComponent { render() { const { query, datasource } = this.props; - const { accountId, webPropertyId, profileId, selectedMetrics, selectedDimensions } = query; + const { accountId, webPropertyId, profileId, selectedMetrics, selectedDimensions, timezone } = query; return ( <> -
- - The accountId is used to identify which GoogleAnalytics is to be accessed or altered. -

- } - > - Account ID -
- datasource.getAccountIds()} - placeholder="Enter Account ID" - value={accountId} - allowCustomValue={true} - onChange={this.onAccountIdChange} - > -
+
+
+ + The accountId is used to identify which GoogleAnalytics is to be accessed or altered. + + } + > + Account ID + + datasource.getAccountIds()} + placeholder="Enter Account ID" + width={6} + value={accountId} + allowCustomValue + onChange={this.onAccountIdChange} + /> + + The webPropertyId is used to identify which GoogleAnalytics is to be accessed or altered. + + } + > + Web Property ID + + datasource.getWebPropertyIds(accountId)} + placeholder="Enter Web Property ID" + value={webPropertyId} + allowCustomValue + onChange={this.onWebPropertyIdChange} + /> + + The profileId is used to identify which GoogleAnalytics is to be accessed or altered. + + } + > + Profile ID + + datasource.getProfileIds(accountId, webPropertyId)} + placeholder="Enter Profile ID" + value={profileId} + allowCustomValue + onChange={this.onProfileIdChange} + /> + GA timeZone}> + Timezone + + {timezone ? timezone : 'determined by profileId'}
-
-
- - The webPropertyId is used to identify which GoogleAnalytics is to be accessed or altered. -

- } - > - Web Property ID -
- datasource.getWebPropertyIds(accountId)} - placeholder="Enter Web Property ID" - value={webPropertyId} - allowCustomValue={true} - onChange={this.onWebPropertyIdChange} - > -
-
+
+ + The metric ga:* + + } + > + Metrics + + datasource.getMetrics(q)} + placeholder={'ga:sessions'} + value={selectedMetrics} + onChange={this.onMetricChange} + backspaceRemovesValue + cacheOptions + noOptionsMessage={'Search Metrics'} + defaultOptions + />
-
-
- - The profileId is used to identify which GoogleAnalytics is to be accessed or altered. This -
- } - > - Profile ID - - datasource.getProfileIds(accountId, webPropertyId)} - placeholder="Enter Profile ID" - value={profileId} - allowCustomValue={true} - onChange={this.onProfileIdChange} - > - -
-
+
+ + The dimensions At least one ga:date* is required. + + } + > + Dimensions + + datasource.getDimensions(q)} + placeholder={'ga:dateHour'} + value={selectedDimensions} + onChange={this.onDimensionChange} + backspaceRemovesValue + cacheOptions + noOptionsMessage={'Search Dimension'} + defaultOptions + />
- -
- - The metric ga:* -

- } - > - Metrics -
- datasource.getMetrics(q)} - placeholder={'ga:sessions'} - value={selectedMetrics} - onChange={this.onMetricChange} - backspaceRemovesValue - cacheOptions - noOptionsMessage={'Search Metrics'} - > -
- -
- - The dimensions At least one ga:date* is required. -

- } - > - Dimension -
- datasource.getDimensions(q)} - placeholder={'ga:dateHour'} - value={selectedDimensions} - onChange={this.onDimensionChange} - backspaceRemovesValue - cacheOptions - noOptionsMessage={'Search Dimension'} - > -
); } diff --git a/src/types.ts b/src/types.ts index ff8582e..1cdc9e7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -38,3 +38,20 @@ export interface GASecureJsonData { profileId?: string; timezone?: string; } + +export interface GAMetadata { + id: string; + kind: string; + attributes: GAMetadataAttribute; +} + +export interface GAMetadataAttribute { + type: string; + dataType: string; + group: string; + status?: string; + uiName: string; + description: string; + allowedInSegments?: string; + addedInAPIVersion?: string; +}