Skip to content

Commit

Permalink
feat: add list time series to query editor
Browse files Browse the repository at this point in the history
  • Loading branch information
ssjagad committed Jul 7, 2024
1 parent 4048b6d commit 188dc69
Show file tree
Hide file tree
Showing 17 changed files with 402 additions and 8 deletions.
9 changes: 9 additions & 0 deletions pkg/framer/fields/field_names.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
38 changes: 38 additions & 0 deletions pkg/framer/fields/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
104 changes: 104 additions & 0 deletions pkg/framer/list_time_series.go
Original file line number Diff line number Diff line change
@@ -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
}
19 changes: 19 additions & 0 deletions pkg/models/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions pkg/models/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
QueryTypeDescribeAsset = "DescribeAsset"
QueryTypeDescribeAssetModel = "DescribeAssetModel"
QueryTypeListAssetProperties = "ListAssetProperties"
QueryTypeListTimeSeries = "ListTimeSeries"
)

const (
Expand Down
1 change: 1 addition & 0 deletions pkg/server/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
23 changes: 23 additions & 0 deletions pkg/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
64 changes: 64 additions & 0 deletions pkg/sitewise/api/list_time_series.go
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 6 additions & 0 deletions pkg/sitewise/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 7 additions & 5 deletions src/RelativeRangeRequestCache/cacheIdUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@ function createSiteWiseQuery(id: number): SitewiseQueriesUnion {
modelId: `mock-model-${id}`,
filter: 'ALL',
aggregates: [AggregateType.AVERAGE],
timeSeriesType: "DISASSOCIATED",
aliasPrefix: "aws/mock/disassociated"
};
}

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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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"]'
])
]);

Expand Down
4 changes: 4 additions & 0 deletions src/RelativeRangeRequestCache/cacheIdUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ function generateSiteWiseQueryCacheId(query: SitewiseQueriesUnion): QueryCacheId
modelId,
filter,
aggregates,
timeSeriesType,
aliasPrefix,
} = query;

/*
Expand Down Expand Up @@ -68,5 +70,7 @@ function generateSiteWiseQueryCacheId(query: SitewiseQueriesUnion): QueryCacheId
modelId,
filter,
aggregates,
timeSeriesType,
aliasPrefix,
]);
}
Loading

0 comments on commit 188dc69

Please sign in to comment.