Skip to content

Commit

Permalink
Export and import applied filters by name
Browse files Browse the repository at this point in the history
  • Loading branch information
b1zzu committed Dec 9, 2021
1 parent 5d549d0 commit a4d8760
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 73 deletions.
20 changes: 10 additions & 10 deletions examples/dashone.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ widgets:
positionx: 0
positiony: 44
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields: []
itemscount: 168
Expand All @@ -26,7 +26,7 @@ widgets:
positionx: 0
positiony: 13
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$executions$passed
Expand All @@ -47,7 +47,7 @@ widgets:
positionx: 0
positiony: 19
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$defects$product_bug$pb001
Expand Down Expand Up @@ -76,7 +76,7 @@ widgets:
positionx: 0
positiony: 25
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$defects$product_bug$pb001
Expand Down Expand Up @@ -105,7 +105,7 @@ widgets:
positionx: 0
positiony: 6
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$executions$total
Expand All @@ -123,7 +123,7 @@ widgets:
positionx: 6
positiony: 0
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$executions$total
Expand Down Expand Up @@ -155,7 +155,7 @@ widgets:
positionx: 0
positiony: 30
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- startTime
Expand Down Expand Up @@ -192,7 +192,7 @@ widgets:
positionx: 6
positiony: 30
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$executions$total
Expand Down Expand Up @@ -226,7 +226,7 @@ widgets:
positionx: 6
positiony: 6
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$executions$total
Expand All @@ -244,7 +244,7 @@ widgets:
positionx: 0
positiony: 0
filters:
- 2
- mk-e2e-test-suite
contentparameters:
contentfields:
- statistics$executions$total
Expand Down
2 changes: 1 addition & 1 deletion pkg/reportportal/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (s *DashboardService) GetByName(projectName, name string) (*Dashboard, *Res
}

if len(dl.Content) == 0 {
return nil, resp, NewDashboardNotFoundError(name, projectName)
return nil, resp, NewDashboardNotFoundError(projectName, name)
}

return dl.Content[0], resp, nil
Expand Down
49 changes: 49 additions & 0 deletions pkg/reportportal/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package reportportal

import "fmt"

type FilterService service

type FilterList struct {
Content []*Filter `json:"content"`
}

type Filter struct {
Share bool `json:"share"`
ID int `json:"id"`
Name string `json:"name"`
// incomplete
}

type FilterNotFoundError struct {
Message string
}

func NewFilterNotFoundError(projectName, filterName string) *DashboardNotFoundError {
return &DashboardNotFoundError{Message: fmt.Sprintf("error filter with name \"%s\" in project \"%s\" not found", filterName, projectName)}
}

func (e *FilterNotFoundError) Error() string {
return e.Message
}

func (s *FilterService) GetByName(projectName, name string) (*Filter, *Response, error) {
u := fmt.Sprintf("v1/%s/filter?filter.eq.name=%s", projectName, name)

req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

fl := new(FilterList)
resp, err := s.client.Do(req, fl)
if err != nil {
return nil, resp, err
}

if len(fl.Content) == 0 {
return nil, resp, NewFilterNotFoundError(projectName, name)
}

return fl.Content[0], resp, nil
}
2 changes: 2 additions & 0 deletions pkg/reportportal/reportportal.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Client struct {
// Services used for talking to different parts of the ReportPortal API.
Dashboard *DashboardService
Widget *WidgetService
Filter *FilterService
}

type service struct {
Expand All @@ -50,6 +51,7 @@ func NewClient(httpClient *http.Client, baseURL string) (*Client, error) {
c.common.client = c
c.Dashboard = (*DashboardService)(&c.common)
c.Widget = (*WidgetService)(&c.common)
c.Filter = (*FilterService)(&c.common)
return c, nil
}

Expand Down
9 changes: 1 addition & 8 deletions pkg/reportportal/widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Widget struct {
Name string `json:"name"`
WidgetType string `json:"widgetType"`
ContentParameters *WidgetContentParameters `json:"contentParameters"`
AppliedFilters []*WidgetAppliedFilter `json:"appliedFilters"`
AppliedFilters []*Filter `json:"appliedFilters"`
Content interface{} `json:"content"` // incomplete
}

Expand All @@ -24,13 +24,6 @@ type WidgetContentParameters struct {
WidgetOptions map[string]interface{} `json:"widgetOptions"`
}

type WidgetAppliedFilter struct {
Share bool `json:"share"`
ID int `json:"id"`
Name string `json:"name"`
// incomplete
}

type NewWidget struct {
Name string `json:"name"`
Description string `json:"description"`
Expand Down
64 changes: 50 additions & 14 deletions pkg/rpdac/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package rpdac

import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"io/ioutil"

"github.com/b1zzu/reportportal-dashboards-as-code/pkg/reportportal"
"gopkg.in/yaml.v2"
)

Expand All @@ -20,7 +22,7 @@ type Widget struct {
WidgetType string `json:"widgetType"`
WidgetSize *WidgetSize `json:"widgetSize"`
WidgetPosition *WidgetPosition `json:"widgetPosition"`
Filters []int `json:"filters"`
Filters []string `json:"filters"`
ContentParameters *WidgetContentParameters `json:"contentParameters"`
}

Expand All @@ -40,36 +42,70 @@ type WidgetContentParameters struct {
WidgetOptions map[string]interface{} `json:"widgetOptions"`
}

func NewDashboard(name string, widgets []*Widget) *Dashboard {
return &Dashboard{Name: name, Widgets: widgets}
func ToDashboard(d *reportportal.Dashboard, widgets []*Widget) *Dashboard {
return &Dashboard{Name: d.Name, Widgets: widgets}
}

func NewWidget(name, description, widgetType string, width, height, positionX, positionY int, filters []int, contentFields []string, itemsCount int, widgetOptions map[string]interface{}) *Widget {
func ToWidget(w *reportportal.Widget, dw *reportportal.DashboardWidget) *Widget {

filters := make([]string, len(w.AppliedFilters))
for j, f := range w.AppliedFilters {
filters[j] = f.Name
}

return &Widget{
Name: name,
Description: description,
WidgetType: widgetType,
WidgetSize: &WidgetSize{Width: width, Height: height},
WidgetPosition: &WidgetPosition{PositionX: positionX, PositionY: positionY},
Name: w.Name,
Description: w.Description,
WidgetType: w.WidgetType,
WidgetSize: &WidgetSize{Width: dw.WidgetSize.Width, Height: dw.WidgetSize.Height},
WidgetPosition: &WidgetPosition{PositionX: dw.WidgetPosition.PositionX, PositionY: dw.WidgetPosition.PositionY},
Filters: filters,
ContentParameters: &WidgetContentParameters{ContentFields: contentFields, ItemsCount: itemsCount, WidgetOptions: widgetOptions},
ContentParameters: &WidgetContentParameters{ContentFields: w.ContentParameters.ContentFields, ItemsCount: w.ContentParameters.ItemsCount, WidgetOptions: w.ContentParameters.WidgetOptions},
}
}

func FromWidget(dashboardHash string, w *Widget, widgetFilters []int) (*reportportal.NewWidget, *reportportal.DashboardWidget) {

nw := &reportportal.NewWidget{
// For the rpdac tool the widget name is not unique across all dashboards, while fore ReportPortal it is,
// by adding the dashboard name sha to the widget name we make it generic
Name: fmt.Sprintf("%s #%s", w.Name, dashboardHash),
Description: w.Description,
Share: true,
WidgetType: w.WidgetType,
Filters: widgetFilters,
ContentParameters: &reportportal.WidgetContentParameters{
ItemsCount: w.ContentParameters.ItemsCount,
ContentFields: w.ContentParameters.ContentFields,
WidgetOptions: w.ContentParameters.WidgetOptions,
},
}

dw := &reportportal.DashboardWidget{
Share: true,
WidgetName: w.Name,
WidgetType: w.WidgetType,
WidgetSize: &reportportal.DashboardWidgetSize{Width: w.WidgetSize.Width, Height: w.WidgetSize.Height},
WidgetPosition: &reportportal.DashboardWidgetPosition{PositionX: w.WidgetPosition.PositionX, PositionY: w.WidgetPosition.PositionY},
}

return nw, dw
}

func LoadDashboardFromFile(file string) (*Dashboard, error) {

b, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}

var d Dashboard
err = yaml.Unmarshal(b, &d)
d := new(Dashboard)
err = yaml.Unmarshal(b, d)
if err != nil {
return nil, err
}

return &d, nil
return d, nil
}

func (d *Dashboard) ToYaml() ([]byte, error) {
Expand Down Expand Up @@ -97,5 +133,5 @@ func (d *Dashboard) WriteToFile(file string) error {
func (d *Dashboard) HashName() string {
h := sha1.New()
io.WriteString(h, d.Name)
return string(h.Sum(nil)[:4])
return hex.EncodeToString(h.Sum(nil))[:4]
}
69 changes: 29 additions & 40 deletions pkg/rpdac/reportportal.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,34 @@ func (r *ReportPortal) GetDashboard(project string, dashboardID int) (*Dashboard
return nil, fmt.Errorf("error retrieving widget %d from project %s: %w", dw.WidgetID, project, err)
}

filters := make([]int, len(w.AppliedFilters))
for j, f := range w.AppliedFilters {
filters[j] = f.ID
}

widgets[i] = NewWidget(
w.Name,
w.Description,
w.WidgetType,
dw.WidgetSize.Width,
dw.WidgetSize.Height,
dw.WidgetPosition.PositionX,
dw.WidgetPosition.PositionY,
filters,
w.ContentParameters.ContentFields,
w.ContentParameters.ItemsCount,
w.ContentParameters.WidgetOptions)
widgets[i] = ToWidget(w, dw)
}

return NewDashboard(d.Name, widgets), nil
return ToDashboard(d, widgets), nil
}

func (r *ReportPortal) CreateDashboard(project string, d *Dashboard) error {

hash := d.HashName()
dashboardHash := d.HashName()

// resolve all filters
filtersMap := make(map[string]int)
for _, w := range d.Widgets {
for _, filterName := range w.Filters {

if _, ok := filtersMap[filterName]; ok {
// filter already resolved
continue
}

f, _, err := r.client.Filter.GetByName(project, filterName)
if err != nil {
return fmt.Errorf("error resolving filter \"%s\" in widget \"%s\" in dashboard \"%s\": %w", filterName, w.Name, d.Name, err)
}

filtersMap[filterName] = f.ID
}
}

dashboardID, _, err := r.client.Dashboard.Create(project, &reportportal.NewDashboard{Name: d.Name, Share: true})
if err != nil {
Expand All @@ -66,34 +69,20 @@ func (r *ReportPortal) CreateDashboard(project string, d *Dashboard) error {

for _, w := range d.Widgets {

nw := &reportportal.NewWidget{
// For the rpdac tool the widget name is not unique across all dashboards, while fore ReportPortal it is,
// by adding the dashboard name sha to the widget name we make it generic
Name: fmt.Sprintf("%s #%s", w.Name, hash),
Description: w.Description,
Share: true,
WidgetType: w.WidgetType,
Filters: w.Filters,
ContentParameters: &reportportal.WidgetContentParameters{
ItemsCount: w.ContentParameters.ItemsCount,
ContentFields: w.ContentParameters.ContentFields,
WidgetOptions: w.ContentParameters.WidgetOptions,
},
filters := make([]int, len(w.Filters))
for j, f := range w.Filters {
filters[j] = filtersMap[f]
}

nw, dw := FromWidget(dashboardHash, w, filters)

widgetID, _, err := r.client.Widget.Post(project, nw)
if err != nil {
return fmt.Errorf("error creating widget %s: %w", w.Name, err)
}
log.Printf("widget %s created with id %d", w.Name, widgetID)

dw := &reportportal.DashboardWidget{
WidgetID: widgetID,
Share: true,
WidgetName: w.Name,
WidgetType: w.WidgetType,
WidgetSize: &reportportal.DashboardWidgetSize{Width: w.WidgetSize.Width, Height: w.WidgetSize.Height},
WidgetPosition: &reportportal.DashboardWidgetPosition{PositionX: w.WidgetPosition.PositionX, PositionY: w.WidgetPosition.PositionY},
}
dw.WidgetID = widgetID

_, _, err = r.client.Dashboard.AddWidget(project, dashboardID, dw)
if err != nil {
Expand Down

0 comments on commit a4d8760

Please sign in to comment.