Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add list time series to query editor #339

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading