From 803433a9ad795879f280f6f3bb9dad6aebcf3c98 Mon Sep 17 00:00:00 2001 From: Sahil Jagad Date: Tue, 2 Jul 2024 13:11:35 -0700 Subject: [PATCH] feat: add list time series to query editor --- pkg/framer/fields/field_names.go | 9 ++ pkg/framer/fields/fields.go | 38 +++++ pkg/framer/list_time_series.go | 104 ++++++++++++++ pkg/models/asset.go | 19 +++ pkg/models/query.go | 1 + pkg/server/datasource.go | 1 + pkg/server/handlers.go | 23 +++ pkg/server/server.go | 1 + pkg/sitewise/api/list_time_series.go | 64 +++++++++ pkg/sitewise/datasource.go | 6 + .../cacheIdUtils.test.ts | 12 +- src/RelativeRangeRequestCache/cacheIdUtils.ts | 4 + src/RelativeRangeRequestCache/types.ts | 6 +- .../query/ListTimeSeriesQueryEditor.tsx | 134 ++++++++++++++++++ src/components/query/QueryEditor.tsx | 5 +- src/queryInfo.ts | 8 ++ src/types.ts | 16 +++ 17 files changed, 443 insertions(+), 8 deletions(-) create mode 100644 pkg/framer/list_time_series.go create mode 100644 pkg/sitewise/api/list_time_series.go create mode 100644 src/components/query/ListTimeSeriesQueryEditor.tsx diff --git a/pkg/framer/fields/field_names.go b/pkg/framer/fields/field_names.go index 626f7ca4..bf98781f 100644 --- a/pkg/framer/fields/field_names.go +++ b/pkg/framer/fields/field_names.go @@ -18,4 +18,13 @@ const ( CompositeModels = "composite_models" AnomalyScore = "anomaly_score" PredictionReason = "prediction_reason" + Alias = "alias" + AssetId = "asset_id" + DataType = "dataType" + DataTypeSpec = "dataTypeSpec" + PropertyId = "propertyId" + TimeSeriesArn = "timeSeriesArn" + TimeSeriesId = "timeSeriesId" + TimeSeriesCreationDate = "timeSeriesCreationDate" + TimeSeriesLastUpdateDate = "timeSeriesLastUpdateDate" ) diff --git a/pkg/framer/fields/fields.go b/pkg/framer/fields/fields.go index 30d40534..dd16029b 100644 --- a/pkg/framer/fields/fields.go +++ b/pkg/framer/fields/fields.go @@ -110,3 +110,41 @@ func PredictionReasonField(length int) *data.Field { func DiagnosticField(length int, assetId string) *data.Field { return NewFieldWithName(assetId, data.FieldTypeFloat64, length) } + +// for time series + +func AliasField(length int) *data.Field { + return NewFieldWithName(Alias, data.FieldTypeString, length) +} + +func AssetIdField(length int) *data.Field { + return NewFieldWithName(AssetId, data.FieldTypeString, length) +} + +func DataTypeField(length int) *data.Field { + return NewFieldWithName(DataType, data.FieldTypeString, length) +} + +func DataTypeSpecField(length int) *data.Field { + return NewFieldWithName(DataTypeSpec, data.FieldTypeString, length) +} + +func PropertyIdField(length int) *data.Field { + return NewFieldWithName(PropertyId, data.FieldTypeString, length) +} + +func TimeSeriesArnField(length int) *data.Field { + return NewFieldWithName(TimeSeriesArn, data.FieldTypeString, length) +} + +func TimeSeriesIdField(length int) *data.Field { + return NewFieldWithName(TimeSeriesId, data.FieldTypeString, length) +} + +func TimeSeriesCreationDateField(length int) *data.Field { + return NewFieldWithName(TimeSeriesCreationDate, data.FieldTypeTime, length) +} + +func TimeSeriesLastUpdateDateField(length int) *data.Field { + return NewFieldWithName(TimeSeriesLastUpdateDate, data.FieldTypeTime, length) +} diff --git a/pkg/framer/list_time_series.go b/pkg/framer/list_time_series.go new file mode 100644 index 00000000..4360fb3c --- /dev/null +++ b/pkg/framer/list_time_series.go @@ -0,0 +1,104 @@ +package framer + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/iot-sitewise-datasource/pkg/framer/fields" + "github.com/grafana/iot-sitewise-datasource/pkg/models" + "github.com/grafana/iot-sitewise-datasource/pkg/sitewise/resource" + +) + +type TimeSeries iotsitewise.ListTimeSeriesOutput + +type timeSeriesSummaryFields struct { + alias *data.Field + assetId *data.Field + dataType *data.Field + dataTypeSpec *data.Field + propertyId *data.Field + timeSeriesArn *data.Field + timeSeriesId *data.Field + timeSeriesCreationDate *data.Field + timeSeriesLastUpdateDate *data.Field +} + +func (f *timeSeriesSummaryFields) fields() data.Fields { + return data.Fields{ + f.alias, + f.assetId, + f.dataType, + f.dataTypeSpec, + f.propertyId, + f.timeSeriesArn, + f.timeSeriesId, + f.timeSeriesCreationDate, + f.timeSeriesLastUpdateDate, + } +} + +func newTimeSeriesSummaryFields(length int) *timeSeriesSummaryFields { + return &timeSeriesSummaryFields{ + alias: fields.AliasField(length), + assetId: fields.AssetIdField(length), + dataType: fields.DataTypeField(length), + dataTypeSpec: fields.DataTypeSpecField(length), + propertyId: fields.PropertyIdField(length), + timeSeriesArn: fields.TimeSeriesArnField(length), + timeSeriesId: fields.TimeSeriesIdField(length), + timeSeriesCreationDate: fields.TimeSeriesCreationDateField(length), + timeSeriesLastUpdateDate: fields.TimeSeriesLastUpdateDateField(length), + } +} + +func (t TimeSeries) Frames(_ context.Context, _ resource.ResourceProvider) (data.Frames, error) { + + length := len(t.TimeSeriesSummaries) + + timeSeriesSummaryFields := newTimeSeriesSummaryFields(length) + + for i, timeSeries := range t.TimeSeriesSummaries { + + + if (timeSeries.Alias != nil) { + timeSeriesSummaryFields.alias.Set(i, *timeSeries.Alias) + } + if (timeSeries.AssetId != nil) { + timeSeriesSummaryFields.assetId.Set(i, *timeSeries.AssetId) + } + if (timeSeries.DataType != nil) { + timeSeriesSummaryFields.dataType.Set(i, *timeSeries.DataType) + } + if (timeSeries.DataTypeSpec != nil) { + timeSeriesSummaryFields.dataTypeSpec.Set(i, *timeSeries.DataTypeSpec) + } + if (timeSeries.PropertyId != nil) { + timeSeriesSummaryFields.propertyId.Set(i, *timeSeries.PropertyId) + } + if (timeSeries.TimeSeriesArn != nil) { + timeSeriesSummaryFields.timeSeriesArn.Set(i, *timeSeries.TimeSeriesArn) + } + if (timeSeries.TimeSeriesId != nil) { + timeSeriesSummaryFields.timeSeriesId.Set(i, *timeSeries.TimeSeriesId) + } + if (timeSeries.TimeSeriesCreationDate != nil) { + timeSeriesSummaryFields.timeSeriesCreationDate.Set(i, *timeSeries.TimeSeriesCreationDate) + } + if (timeSeries.TimeSeriesLastUpdateDate != nil) { + timeSeriesSummaryFields.timeSeriesLastUpdateDate.Set(i, *timeSeries.TimeSeriesLastUpdateDate) + } + } + + frame := data.NewFrame("", timeSeriesSummaryFields.fields()...) + + frame.Meta = &data.FrameMeta{ + Custom: models.SitewiseCustomMeta{ + NextToken: aws.StringValue(t.NextToken), + }, + } + + return data.Frames{frame}, nil +} diff --git a/pkg/models/asset.go b/pkg/models/asset.go index 6ba115a9..bf37d209 100644 --- a/pkg/models/asset.go +++ b/pkg/models/asset.go @@ -20,6 +20,13 @@ type ListAssetsQuery struct { Filter string `json:"filter,omitempty"` } +type ListTimeSeriesQuery struct { + BaseQuery + TimeSeriesType string `json:"timeSeriesType,omitempty"` + AssetId string `json:"assetId,omitempty"` + AliasPrefix string `json:"aliasPrefix,omitempty"` +} + type ListAssociatedAssetsQuery struct { BaseQuery HierarchyId string `json:"hierarchyId,omitempty"` @@ -70,6 +77,18 @@ func GetListAssetsQuery(dq *backend.DataQuery) (*ListAssetsQuery, error) { return query, nil } +func GetListTimeSeriesQuery(dq *backend.DataQuery) (*ListTimeSeriesQuery, error) { + query := &ListTimeSeriesQuery{} + if err := json.Unmarshal(dq.JSON, query); err != nil { + return nil, err + } + + // add on the DataQuery params + query.MaxDataPoints = dq.MaxDataPoints + query.QueryType = dq.QueryType + return query, nil +} + func GetListAssociatedAssetsQuery(dq *backend.DataQuery) (*ListAssociatedAssetsQuery, error) { query := &ListAssociatedAssetsQuery{} if err := json.Unmarshal(dq.JSON, query); err != nil { diff --git a/pkg/models/query.go b/pkg/models/query.go index 2725d801..f81cad7d 100644 --- a/pkg/models/query.go +++ b/pkg/models/query.go @@ -17,6 +17,7 @@ const ( QueryTypeDescribeAsset = "DescribeAsset" QueryTypeDescribeAssetModel = "DescribeAssetModel" QueryTypeListAssetProperties = "ListAssetProperties" + QueryTypeListTimeSeries = "ListTimeSeries" ) const ( diff --git a/pkg/server/datasource.go b/pkg/server/datasource.go index c7d89787..244643b3 100644 --- a/pkg/server/datasource.go +++ b/pkg/server/datasource.go @@ -20,4 +20,5 @@ type Datasource interface { HandleDescribeAssetQuery(ctx context.Context, req *backend.QueryDataRequest, query *models.DescribeAssetQuery) (data.Frames, error) HandleListAssociatedAssetsQuery(ctx context.Context, req *backend.QueryDataRequest, query *models.ListAssociatedAssetsQuery) (data.Frames, error) HandleDescribeAssetModelQuery(ctx context.Context, req *backend.QueryDataRequest, query *models.DescribeAssetModelQuery) (data.Frames, error) + HandleListTimeSeriesQuery(ctx context.Context, req *backend.QueryDataRequest, query *models.ListTimeSeriesQuery) (data.Frames, error) } diff --git a/pkg/server/handlers.go b/pkg/server/handlers.go index 59a1e903..50f7d495 100644 --- a/pkg/server/handlers.go +++ b/pkg/server/handlers.go @@ -49,6 +49,10 @@ func (s *Server) HandleDescribeAsset(ctx context.Context, req *backend.QueryData return processQueries(ctx, req, s.handleDescribeAssetQuery), nil } +func (s *Server) HandleListTimeSeries(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { + return processQueries(ctx, req, s.handleListTimeSeriesQuery), nil +} + func (s *Server) HandleListAssetProperties(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { return processQueries(ctx, req, s.handleListAssetPropertiesQuery), nil } @@ -230,6 +234,25 @@ func (s *Server) handleListAssociatedAssetsQuery(ctx context.Context, req *backe } } +func (s *Server) handleListTimeSeriesQuery(ctx context.Context, req *backend.QueryDataRequest, q backend.DataQuery) backend.DataResponse { + query, err := models.GetListTimeSeriesQuery(&q) + + if err != nil { + return DataResponseErrorUnmarshal(err) + } + + frames, err := s.Datasource.HandleListTimeSeriesQuery(ctx, req, query) + + if err != nil { + return DataResponseErrorRequestFailed(err) + } + + return backend.DataResponse{ + Frames: frames, + Error: nil, + } +} + func (s *Server) handleDescribeAssetQuery(ctx context.Context, req *backend.QueryDataRequest, q backend.DataQuery) backend.DataResponse { query, err := models.GetDescribeAssetQuery(&q) diff --git a/pkg/server/server.go b/pkg/server/server.go index 5aa5c904..068627b7 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -63,6 +63,7 @@ func getQueryHandlers(s *Server) *datasource.QueryTypeMux { mux.HandleFunc(models.QueryTypeListAssets, s.HandleListAssets) mux.HandleFunc(models.QueryTypeDescribeAsset, s.HandleDescribeAsset) mux.HandleFunc(models.QueryTypeListAssetProperties, s.HandleListAssetProperties) + mux.HandleFunc(models.QueryTypeListTimeSeries, s.HandleListTimeSeries) return mux } diff --git a/pkg/sitewise/api/list_time_series.go b/pkg/sitewise/api/list_time_series.go new file mode 100644 index 00000000..2a965c72 --- /dev/null +++ b/pkg/sitewise/api/list_time_series.go @@ -0,0 +1,64 @@ +package api + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/grafana/iot-sitewise-datasource/pkg/framer" + "github.com/grafana/iot-sitewise-datasource/pkg/models" + "github.com/grafana/iot-sitewise-datasource/pkg/sitewise/client" + "github.com/grafana/iot-sitewise-datasource/pkg/util" + +) + +func ListTimeSeries(ctx context.Context, client client.SitewiseClient, query models.ListTimeSeriesQuery) (*framer.TimeSeries, error) { + + var ( + timeSeriesType *string + assetId *string = util.GetAssetId(query.BaseQuery) + aliasPrefix *string + ) + + if query.TimeSeriesType != "" { + // if user wants to see all timeseries data do not filter on type + if query.TimeSeriesType == "ALL" { + timeSeriesType = nil + } else { + timeSeriesType = aws.String(query.TimeSeriesType) + } + } + + if query.AliasPrefix != "" { + aliasPrefix = aws.String(query.AliasPrefix) + } + + if query.TimeSeriesType == "DISASSOCIATED" { + // cannot filter on assetId for disassociated data + assetId = nil + } + + if query.TimeSeriesType == "ASSOCIATED" { + // cannot filter by alias prefix on associated data + aliasPrefix = nil + } + + resp, err := client.ListTimeSeriesWithContext(ctx, &iotsitewise.ListTimeSeriesInput{ + AssetId: assetId, + TimeSeriesType: timeSeriesType, + AliasPrefix: aliasPrefix, + MaxResults: aws.Int64(250), + NextToken: getNextToken(query.BaseQuery), + }) + + + if err != nil { + return nil, err + } + + return &framer.TimeSeries{ + TimeSeriesSummaries: resp.TimeSeriesSummaries, + NextToken: resp.NextToken, + }, nil +} diff --git a/pkg/sitewise/datasource.go b/pkg/sitewise/datasource.go index 9f31fdfa..d3391d32 100644 --- a/pkg/sitewise/datasource.go +++ b/pkg/sitewise/datasource.go @@ -210,6 +210,12 @@ func (ds *Datasource) HandleListAssetsQuery(ctx context.Context, req *backend.Qu }) } +func (ds *Datasource) HandleListTimeSeriesQuery(ctx context.Context, req *backend.QueryDataRequest, query *models.ListTimeSeriesQuery) (data.Frames, error) { + return ds.invoke(ctx, req, &query.BaseQuery, func(ctx context.Context, sw client.SitewiseClient) (framer.Framer, error) { + return api.ListTimeSeries(ctx, sw, *query) + }) +} + func (ds *Datasource) HandleDescribeAssetQuery(ctx context.Context, req *backend.QueryDataRequest, query *models.DescribeAssetQuery) (data.Frames, error) { return ds.invoke(ctx, req, &query.BaseQuery, func(ctx context.Context, sw client.SitewiseClient) (framer.Framer, error) { return api.DescribeAsset(ctx, sw, *query) diff --git a/src/RelativeRangeRequestCache/cacheIdUtils.test.ts b/src/RelativeRangeRequestCache/cacheIdUtils.test.ts index 6b35b4ce..e7a26114 100644 --- a/src/RelativeRangeRequestCache/cacheIdUtils.test.ts +++ b/src/RelativeRangeRequestCache/cacheIdUtils.test.ts @@ -28,6 +28,8 @@ function createSiteWiseQuery(id: number): SitewiseQueriesUnion { modelId: `mock-model-${id}`, filter: 'ALL', aggregates: [AggregateType.AVERAGE], + timeSeriesType: "DISASSOCIATED", + aliasPrefix: "aws/mock/disassociated" }; } @@ -35,8 +37,8 @@ describe('generateSiteWiseQueriesCacheId()', () => { it('parses SiteWise Queries into cache Id', () => { const actualId = generateSiteWiseQueriesCacheId([createSiteWiseQuery(1), createSiteWiseQuery(2)]); const expectedId = JSON.stringify([ - '["PropertyValueHistory","us-west-2","table","mock-asset-id-1",["mock-asset-id-1"],"mock-property-id-1","mock-property-alias-1","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-1","mock-model-1","ALL",["AVERAGE"]]', - '["PropertyValueHistory","us-west-2","table","mock-asset-id-2",["mock-asset-id-2"],"mock-property-id-2","mock-property-alias-2","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-2","mock-model-2","ALL",["AVERAGE"]]' + '["PropertyValueHistory","us-west-2","table","mock-asset-id-1",["mock-asset-id-1"],"mock-property-id-1","mock-property-alias-1","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-1","mock-model-1","ALL",["AVERAGE"],"DISASSOCIATED","aws/mock/disassociated"]', + '["PropertyValueHistory","us-west-2","table","mock-asset-id-2",["mock-asset-id-2"],"mock-property-id-2","mock-property-alias-2","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-2","mock-model-2","ALL",["AVERAGE"],"DISASSOCIATED","aws/mock/disassociated"]' ]); expect(actualId).toEqual(expectedId); @@ -83,7 +85,7 @@ describe('generateSiteWiseQueriesCacheId()', () => { }; const actualId = generateSiteWiseQueriesCacheId([query]); const expectedId = JSON.stringify([ - '["ListAssets",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]', + '["ListAssets",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]', ]); expect(actualId).toEqual(expectedId); @@ -113,8 +115,8 @@ describe('generateSiteWiseRequestCacheId()', () => { const expectedId = JSON.stringify([ 'now-15m', JSON.stringify([ - '["PropertyValueHistory","us-west-2","table","mock-asset-id-1",["mock-asset-id-1"],"mock-property-id-1","mock-property-alias-1","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-1","mock-model-1","ALL",["AVERAGE"]]', - '["PropertyValueHistory","us-west-2","table","mock-asset-id-2",["mock-asset-id-2"],"mock-property-id-2","mock-property-alias-2","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-2","mock-model-2","ALL",["AVERAGE"]]' + '["PropertyValueHistory","us-west-2","table","mock-asset-id-1",["mock-asset-id-1"],"mock-property-id-1","mock-property-alias-1","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-1","mock-model-1","ALL",["AVERAGE"],"DISASSOCIATED","aws/mock/disassociated"]', + '["PropertyValueHistory","us-west-2","table","mock-asset-id-2",["mock-asset-id-2"],"mock-property-id-2","mock-property-alias-2","ANY","AUTO",true,true,1000,"grafana-iot-sitewise-datasource","mock-datasource-uid","ASCENDING",true,"mock-hierarchy-2","mock-model-2","ALL",["AVERAGE"],"DISASSOCIATED","aws/mock/disassociated"]' ]) ]); diff --git a/src/RelativeRangeRequestCache/cacheIdUtils.ts b/src/RelativeRangeRequestCache/cacheIdUtils.ts index b7aab375..75e49bf6 100644 --- a/src/RelativeRangeRequestCache/cacheIdUtils.ts +++ b/src/RelativeRangeRequestCache/cacheIdUtils.ts @@ -41,6 +41,8 @@ function generateSiteWiseQueryCacheId(query: SitewiseQueriesUnion): QueryCacheId modelId, filter, aggregates, + timeSeriesType, + aliasPrefix, } = query; /* @@ -68,5 +70,7 @@ function generateSiteWiseQueryCacheId(query: SitewiseQueriesUnion): QueryCacheId modelId, filter, aggregates, + timeSeriesType, + aliasPrefix, ]); } diff --git a/src/RelativeRangeRequestCache/types.ts b/src/RelativeRangeRequestCache/types.ts index 9e8b9a6e..6e3d0078 100644 --- a/src/RelativeRangeRequestCache/types.ts +++ b/src/RelativeRangeRequestCache/types.ts @@ -1,5 +1,5 @@ import { DataFrame } from '@grafana/data'; -import { AssetPropertyAggregatesQuery, AssetPropertyValueHistoryQuery, ListAssetsQuery, ListAssociatedAssetsQuery, QueryType, SitewiseQuery } from 'types'; +import { AssetPropertyAggregatesQuery, AssetPropertyValueHistoryQuery, ListAssetsQuery, ListAssociatedAssetsQuery, ListTimeSeriesQuery, QueryType, SitewiseQuery } from 'types'; const TIME_SERIES_QUERY_TYPES = new Set([ QueryType.PropertyAggregate, @@ -33,4 +33,6 @@ export type SitewiseQueriesUnion = SitewiseQuery & Partial> & Partial> & Partial> - & Partial>; + & Partial> + & Partial> + & Partial>; diff --git a/src/components/query/ListTimeSeriesQueryEditor.tsx b/src/components/query/ListTimeSeriesQueryEditor.tsx new file mode 100644 index 00000000..28fbecee --- /dev/null +++ b/src/components/query/ListTimeSeriesQueryEditor.tsx @@ -0,0 +1,134 @@ +import React, { ChangeEvent, useState } from 'react'; +import { SelectableValue } from '@grafana/data'; +import { ListTimeSeriesQuery } from 'types'; +import { InlineField, Input, Select } from '@grafana/ui'; +import { SitewiseQueryEditorProps } from './types'; +import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/experimental'; +import { firstLabelWith } from './QueryEditor'; + +interface Props extends SitewiseQueryEditorProps { + newFormStylingEnabled?: boolean; +} + +const timeSeriesTypes = [ + { + label: 'ALL', + value: 'ALL', + description: 'All time series data', + }, + { + label: 'ASSOCIATED', + value: 'ASSOCIATED', + description: "The time series is associated with an asset property.", + }, + { label: 'DISASSOCIATED', value: 'DISASSOCIATED', description: "The time series isn't associated with any asset property." }, +]; + +export const ListTimeSeriesQueryEditorFunction = (props: Props) => { + + const [lastInput, setLastInput] = useState() + + const onAliasPrefixChange = (e: ChangeEvent) => { + const { onChange, query } = props; + setLastInput("prefix") + onChange({ ...query, aliasPrefix: e.target.value }); + }; + + const onAssetIdChange = (e: ChangeEvent) => { + const { onChange, query } = props; + setLastInput("id") + onChange({ ...query, assetId: e.target.value }); + }; + + const onTimeSeriesTypeChange = (sel: SelectableValue) => { + const { onChange, query } = props; + if (sel.value === 'ALL' && lastInput === "prefix"){ + onChange({ ...query, timeSeriesType: sel.value as 'ASSOCIATED' | 'DISASSOCIATED' | 'ALL' , assetId: undefined}); + } + else if (sel.value === 'ALL' && lastInput === "id"){ + onChange({ ...query, timeSeriesType: sel.value as 'ASSOCIATED' | 'DISASSOCIATED' | 'ALL' , aliasPrefix: undefined}); + } else { + onChange({ ...query, timeSeriesType: sel.value as 'ASSOCIATED' | 'DISASSOCIATED' | 'ALL' }); + } + + }; + + const { query, newFormStylingEnabled } = props; + + return newFormStylingEnabled ? + ( + + + + + + + + + + + ) : ( + <> +
+ + + +
} + {!Boolean(query.timeSeriesType === "DISASSOCIATED") && +
+ + + +
} + + ); +}; + + + diff --git a/src/components/query/QueryEditor.tsx b/src/components/query/QueryEditor.tsx index 1585cdca..fc24b47b 100644 --- a/src/components/query/QueryEditor.tsx +++ b/src/components/query/QueryEditor.tsx @@ -2,7 +2,7 @@ import defaults from 'lodash/defaults'; import React from 'react'; import { QueryEditorProps, SelectableValue } from '@grafana/data'; import { DataSource } from 'DataSource'; -import { SitewiseQuery, SitewiseOptions, QueryType, ListAssetsQuery } from 'types'; +import { SitewiseQuery, SitewiseOptions, QueryType, ListAssetsQuery, ListTimeSeriesQuery } from 'types'; import { Icon, LinkButton, Select } from '@grafana/ui'; import { QueryTypeInfo, siteWiseQueryTypes, changeQueryType } from 'queryInfo'; import { standardRegionOptions } from 'regions'; @@ -11,6 +11,7 @@ import { PropertyQueryEditor } from './PropertyQueryEditor'; import { EditorField, EditorFieldGroup, EditorRow, EditorRows } from '@grafana/experimental'; import { QueryEditorHeader } from '@grafana/aws-sdk'; import { ClientCacheRow } from './ClientCacheRow'; +import { ListTimeSeriesQueryEditorFunction } from './ListTimeSeriesQueryEditor'; type Props = QueryEditorProps; @@ -59,6 +60,8 @@ export function QueryEditor(props: Props) { return null; // nothing required case QueryType.ListAssets: return ; + case QueryType.ListTimeSeries: + return case QueryType.ListAssociatedAssets: case QueryType.PropertyValue: case QueryType.PropertyInterpolated: diff --git a/src/queryInfo.ts b/src/queryInfo.ts index 7ac5c9ab..94b52a0b 100644 --- a/src/queryInfo.ts +++ b/src/queryInfo.ts @@ -14,6 +14,7 @@ import { AssetPropertyInfo, ListAssociatedAssetsQuery, isListAssociatedAssetsQuery, + ListTimeSeriesQuery } from './types'; export interface QueryTypeInfo extends SelectableValue { @@ -80,6 +81,13 @@ export const siteWiseQueryTypes: QueryTypeInfo[] = [ defaultQuery: {} as ListAssociatedAssetsQuery, helpURL: 'https://docs.aws.amazon.com/iot-sitewise/latest/APIReference/API_ListAssociatedAssets.html', }, + { + label: 'List time series', + value: QueryType.ListTimeSeries, + description: 'Retrieves a paginated list of time series (data streams)', + defaultQuery: {} as ListTimeSeriesQuery, + helpURL: 'https://docs.aws.amazon.com/iot-sitewise/latest/APIReference/API_ListTimeSeries.html', + }, ]; export function changeQueryType(q: SitewiseQuery, info: QueryTypeInfo): SitewiseQuery { diff --git a/src/types.ts b/src/types.ts index b85baf6b..c146e055 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,6 +13,7 @@ export enum QueryType { PropertyValueHistory = 'PropertyValueHistory', PropertyAggregate = 'PropertyAggregate', PropertyInterpolated = 'PropertyInterpolated', + ListTimeSeries = "ListTimeSeries" } export enum SiteWiseQuality { @@ -193,6 +194,21 @@ export function isAssetPropertyInterpolatedQuery(q?: SitewiseQuery): q is AssetP return q?.queryType === QueryType.PropertyInterpolated; } +/** + * {@link https://docs.aws.amazon.com/iot-sitewise/latest/APIReference/API_ListTimeSeries.html} + */ + +export interface ListTimeSeriesQuery extends SitewiseQuery { + queryType: QueryType.ListTimeSeries; + aliasPrefix?: string; + assetId?: string; + timeSeriesType?: "ASSOCIATED" | "DISASSOCIATED" | "ALL"; +} + +export function isLIstTimeSeriesQuery(q?: SitewiseQuery): q is ListTimeSeriesQuery { + return q?.queryType === QueryType.ListTimeSeries; +} + export function isPropertyQueryType(queryType?: QueryType): boolean { return ( queryType === QueryType.PropertyAggregate ||