You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by ab...@apache.org on 2024/01/11 08:20:05 UTC

(incubator-devlake) branch jira-filter updated: feat: add filter jql checker

This is an automated email from the ASF dual-hosted git repository.

abeizn pushed a commit to branch jira-filter
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/jira-filter by this push:
     new 715466ee0 feat: add filter jql checker
715466ee0 is described below

commit 715466ee0d368031e19450a0954436d699a2a5f9
Author: abeizn <zi...@merico.dev>
AuthorDate: Thu Jan 11 16:16:31 2024 +0800

    feat: add filter jql checker
---
 backend/plugins/jira/impl/impl.go                  |   4 +
 backend/plugins/jira/models/board.go               |   1 +
 .../migrationscripts/20240111_add_filter_jql.go    |  46 +++++
 .../jira/models/migrationscripts/register.go       |   1 +
 .../jira/tasks/board_filter_begin_collector.go     | 211 +++++++++++++++++++++
 .../jira/tasks/board_filter_end_collector.go       |  84 ++++++++
 6 files changed, 347 insertions(+)

diff --git a/backend/plugins/jira/impl/impl.go b/backend/plugins/jira/impl/impl.go
index 6686b775a..259361475 100644
--- a/backend/plugins/jira/impl/impl.go
+++ b/backend/plugins/jira/impl/impl.go
@@ -103,6 +103,8 @@ func (p Jira) Name() string {
 
 func (p Jira) SubTaskMetas() []plugin.SubTaskMeta {
 	return []plugin.SubTaskMeta{
+		tasks.CollectBoardFilterBeginMeta,
+
 		tasks.CollectStatusMeta,
 		tasks.ExtractStatusMeta,
 
@@ -156,6 +158,8 @@ func (p Jira) SubTaskMetas() []plugin.SubTaskMeta {
 
 		tasks.ExtractAccountsMeta,
 		tasks.ConvertAccountsMeta,
+
+		tasks.CollectBoardFilterEndMeta,
 	}
 }
 
diff --git a/backend/plugins/jira/models/board.go b/backend/plugins/jira/models/board.go
index ad4ed2b0d..267c1cdc0 100644
--- a/backend/plugins/jira/models/board.go
+++ b/backend/plugins/jira/models/board.go
@@ -33,6 +33,7 @@ type JiraBoard struct {
 	Name         string `json:"name" mapstructure:"name" gorm:"type:varchar(255)"`
 	Self         string `json:"self" mapstructure:"self" gorm:"type:varchar(255)"`
 	Type         string `json:"type" mapstructure:"type" gorm:"type:varchar(100)"`
+	Jql          string `json:"jql" mapstructure:"jql"`
 }
 
 func (b JiraBoard) ScopeId() string {
diff --git a/backend/plugins/jira/models/migrationscripts/20240111_add_filter_jql.go b/backend/plugins/jira/models/migrationscripts/20240111_add_filter_jql.go
new file mode 100644
index 000000000..9528cb0d1
--- /dev/null
+++ b/backend/plugins/jira/models/migrationscripts/20240111_add_filter_jql.go
@@ -0,0 +1,46 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+	"github.com/apache/incubator-devlake/core/context"
+	"github.com/apache/incubator-devlake/core/errors"
+	"github.com/apache/incubator-devlake/helpers/migrationhelper"
+)
+
+type JiraBoard20240111 struct {
+	Jql string
+}
+
+func (JiraBoard20240111) TableName() string {
+	return "_tool_jira_boards"
+}
+
+type addFilterJQL struct{}
+
+func (script *addFilterJQL) Up(basicRes context.BasicRes) errors.Error {
+	return migrationhelper.AutoMigrateTables(basicRes, &JiraBoard20240111{})
+}
+
+func (*addFilterJQL) Version() uint64 {
+	return 20240111113738
+}
+
+func (*addFilterJQL) Name() string {
+	return "add jql to _tool_jira_boards"
+}
diff --git a/backend/plugins/jira/models/migrationscripts/register.go b/backend/plugins/jira/models/migrationscripts/register.go
index c900f202e..b4c410c9f 100644
--- a/backend/plugins/jira/models/migrationscripts/register.go
+++ b/backend/plugins/jira/models/migrationscripts/register.go
@@ -46,5 +46,6 @@ func All() []plugin.MigrationScript {
 		new(dropIssueAllFields),
 		new(modifyIssueRelationship),
 		new(addComponents20230412),
+		new(addFilterJQL),
 	}
 }
diff --git a/backend/plugins/jira/tasks/board_filter_begin_collector.go b/backend/plugins/jira/tasks/board_filter_begin_collector.go
new file mode 100644
index 000000000..252289a9d
--- /dev/null
+++ b/backend/plugins/jira/tasks/board_filter_begin_collector.go
@@ -0,0 +1,211 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+	"fmt"
+
+	"github.com/apache/incubator-devlake/core/dal"
+	"github.com/apache/incubator-devlake/core/errors"
+	"github.com/apache/incubator-devlake/core/plugin"
+	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/jira/models"
+)
+
+var CollectBoardFilterBeginMeta = plugin.SubTaskMeta{
+	Name:             "collectBoardFilterBegin",
+	EntryPoint:       CollectBoardFilterBegin,
+	EnabledByDefault: true,
+	Description:      "Jira board filter jql checker before runnig",
+	DomainTypes:      plugin.DOMAIN_TYPES,
+}
+
+func CollectBoardFilterBegin(taskCtx plugin.SubTaskContext) errors.Error {
+	data := taskCtx.GetData().(*JiraTaskData)
+	logger := taskCtx.GetLogger()
+	db := taskCtx.GetDal()
+	logger.Info("collect board in collectBoardFilterBegin: %d", data.Options.BoardId)
+
+	// get board filter id
+	url := fmt.Sprintf("agile/1.0/board/%d/configuration", data.Options.BoardId)
+	boardConfiguration, err := data.ApiClient.Get(url, nil, nil)
+	if err != nil {
+		return err
+	}
+	bc := &BoardConfiguration{}
+	err = helper.UnmarshalResponse(boardConfiguration, bc)
+	if err != nil {
+		return err
+	}
+	filterId := bc.Filter.ID
+	logger.Info("collect board filter:%s", filterId)
+
+	// get board filter jql
+	url = fmt.Sprintf("api/2/filter/%s", filterId)
+	filterInfo, err := data.ApiClient.Get(url, nil, nil)
+	if err != nil {
+		return err
+	}
+	fi := &FilterInfo{}
+	err = helper.UnmarshalResponse(filterInfo, fi)
+	if err != nil {
+		return err
+	}
+	jql := fi.Jql
+	logger.Info("collect board filter jql:%s", jql)
+
+	var record models.JiraBoard
+	err = db.First(&record, dal.Where("connection_id = ? AND board_id = ? ", data.Options.ConnectionId, data.Options.BoardId))
+	if err != nil {
+		return errors.Default.Wrap(err, fmt.Sprintf("error finding record in _tool_jira_boards table for connection_id:%d board_id:%d", data.Options.ConnectionId, data.Options.BoardId))
+	}
+
+	// full sync
+	syncPolicy := taskCtx.TaskContext().SyncPolicy()
+	if syncPolicy != nil && syncPolicy.FullSync {
+		if record.Jql != jql {
+			record.Jql = jql
+			err = db.Update(&record, dal.Where("connection_id = ? AND board_id = ? ", data.Options.ConnectionId, data.Options.BoardId))
+			if err != nil {
+				return errors.Default.Wrap(err, fmt.Sprintf("error updating record in _tool_jira_boards table for connection_id:%d board_id:%d", data.Options.ConnectionId, data.Options.BoardId))
+			}
+		}
+		return nil
+	}
+	// first run
+	if record.Jql == "" {
+		record.Jql = jql
+		err = db.Update(&record, dal.Where("connection_id = ? AND board_id = ? ", data.Options.ConnectionId, data.Options.BoardId))
+		if err != nil {
+			return errors.Default.Wrap(err, fmt.Sprintf("error updating record in _tool_jira_boards table for connection_id:%d board_id:%d", data.Options.ConnectionId, data.Options.BoardId))
+		}
+		return nil
+	}
+	// no change
+	if record.Jql == jql {
+		return nil
+	}
+	// change
+	if record.Jql != jql {
+		return errors.Default.New(fmt.Sprintf("board filter jql has changed for connection_id:%d board_id:%d, please use fullSync mode!!!", data.Options.ConnectionId, data.Options.BoardId))
+	}
+
+	return nil
+}
+
+type BoardConfiguration struct {
+	ID       int    `json:"id"`
+	Name     string `json:"name"`
+	Type     string `json:"type"`
+	Self     string `json:"self"`
+	Location struct {
+		Type string `json:"type"`
+		Key  string `json:"key"`
+		ID   string `json:"id"`
+		Self string `json:"self"`
+		Name string `json:"name"`
+	} `json:"location"`
+	Filter struct {
+		ID   string `json:"id"`
+		Self string `json:"self"`
+	} `json:"filter"`
+	ColumnConfig struct {
+		Columns []struct {
+			Name     string `json:"name"`
+			Statuses []struct {
+				ID   string `json:"id"`
+				Self string `json:"self"`
+			} `json:"statuses"`
+		} `json:"columns"`
+		ConstraintType string `json:"constraintType"`
+	} `json:"columnConfig"`
+	Estimation struct {
+		Type  string `json:"type"`
+		Field struct {
+			FieldID     string `json:"fieldId"`
+			DisplayName string `json:"displayName"`
+		} `json:"field"`
+	} `json:"estimation"`
+	Ranking struct {
+		RankCustomFieldID int `json:"rankCustomFieldId"`
+	} `json:"ranking"`
+}
+
+type FilterInfo struct {
+	Self        string `json:"self"`
+	ID          string `json:"id"`
+	Name        string `json:"name"`
+	Description string `json:"description"`
+	Owner       struct {
+		Self       string `json:"self"`
+		AccountID  string `json:"accountId"`
+		AvatarUrls struct {
+			Four8X48  string `json:"48x48"`
+			Two4X24   string `json:"24x24"`
+			One6X16   string `json:"16x16"`
+			Three2X32 string `json:"32x32"`
+		} `json:"avatarUrls"`
+		DisplayName string `json:"displayName"`
+		Active      bool   `json:"active"`
+	} `json:"owner"`
+	Jql              string `json:"jql"`
+	ViewURL          string `json:"viewUrl"`
+	SearchURL        string `json:"searchUrl"`
+	Favourite        bool   `json:"favourite"`
+	FavouritedCount  int    `json:"favouritedCount"`
+	SharePermissions []struct {
+		ID      int    `json:"id"`
+		Type    string `json:"type"`
+		Project struct {
+			Self         string `json:"self"`
+			ID           string `json:"id"`
+			Key          string `json:"key"`
+			AssigneeType string `json:"assigneeType"`
+			Name         string `json:"name"`
+			Roles        struct {
+			} `json:"roles"`
+			AvatarUrls struct {
+				Four8X48  string `json:"48x48"`
+				Two4X24   string `json:"24x24"`
+				One6X16   string `json:"16x16"`
+				Three2X32 string `json:"32x32"`
+			} `json:"avatarUrls"`
+			ProjectTypeKey string `json:"projectTypeKey"`
+			Simplified     bool   `json:"simplified"`
+			Style          string `json:"style"`
+			Properties     struct {
+			} `json:"properties"`
+		} `json:"project"`
+	} `json:"sharePermissions"`
+	EditPermissions []any `json:"editPermissions"`
+	IsWritable      bool  `json:"isWritable"`
+	SharedUsers     struct {
+		Size       int   `json:"size"`
+		Items      []any `json:"items"`
+		MaxResults int   `json:"max-results"`
+		StartIndex int   `json:"start-index"`
+		EndIndex   int   `json:"end-index"`
+	} `json:"sharedUsers"`
+	Subscriptions struct {
+		Size       int   `json:"size"`
+		Items      []any `json:"items"`
+		MaxResults int   `json:"max-results"`
+		StartIndex int   `json:"start-index"`
+		EndIndex   int   `json:"end-index"`
+	} `json:"subscriptions"`
+}
diff --git a/backend/plugins/jira/tasks/board_filter_end_collector.go b/backend/plugins/jira/tasks/board_filter_end_collector.go
new file mode 100644
index 000000000..caa9dfaae
--- /dev/null
+++ b/backend/plugins/jira/tasks/board_filter_end_collector.go
@@ -0,0 +1,84 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+	"fmt"
+
+	"github.com/apache/incubator-devlake/core/dal"
+	"github.com/apache/incubator-devlake/core/errors"
+	"github.com/apache/incubator-devlake/core/plugin"
+	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/jira/models"
+)
+
+var CollectBoardFilterEndMeta = plugin.SubTaskMeta{
+	Name:             "collectBoardFilterEnd",
+	EntryPoint:       CollectBoardFilterEnd,
+	EnabledByDefault: true,
+	Description:      "Jira board filter jql checker after runnig",
+	DomainTypes:      plugin.DOMAIN_TYPES,
+}
+
+func CollectBoardFilterEnd(taskCtx plugin.SubTaskContext) errors.Error {
+	data := taskCtx.GetData().(*JiraTaskData)
+	logger := taskCtx.GetLogger()
+	db := taskCtx.GetDal()
+	logger.Info("collect board in collectBoardFilterEnd: %d", data.Options.BoardId)
+
+	// get board filter id
+	url := fmt.Sprintf("agile/1.0/board/%d/configuration", data.Options.BoardId)
+	boardConfiguration, err := data.ApiClient.Get(url, nil, nil)
+	if err != nil {
+		return err
+	}
+	bc := &BoardConfiguration{}
+	err = helper.UnmarshalResponse(boardConfiguration, bc)
+	if err != nil {
+		return err
+	}
+	filterId := bc.Filter.ID
+	logger.Info("collect board filter:%s", filterId)
+
+	// get board filter jql
+	url = fmt.Sprintf("api/2/filter/%s", filterId)
+	filterInfo, err := data.ApiClient.Get(url, nil, nil)
+	if err != nil {
+		return err
+	}
+	fi := &FilterInfo{}
+	err = helper.UnmarshalResponse(filterInfo, fi)
+	if err != nil {
+		return err
+	}
+	jql := fi.Jql
+	logger.Info("collect board filter jql:%s", jql)
+
+	// should not change
+	var record models.JiraBoard
+	err = db.First(&record, dal.Where("connection_id = ? AND board_id = ? ", data.Options.ConnectionId, data.Options.BoardId))
+	if err != nil {
+		return errors.Default.Wrap(err, fmt.Sprintf("error finding record in _tool_jira_boards table for connection_id:%d board_id:%d", data.Options.ConnectionId, data.Options.BoardId))
+	}
+
+	if record.Jql != jql {
+		return errors.Default.New(fmt.Sprintf("board filter jql has changed for connection_id:%d board_id:%d, please use fullSync mode!!!", data.Options.ConnectionId, data.Options.BoardId))
+	}
+
+	return nil
+}