This is an automated email from the ASF dual-hosted git repository.
likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 8d24b4059 refactor(framework): use generic to optimize (#4659)
8d24b4059 is described below
commit 8d24b40590cf01b199faf52a5f12408db80aa1eb
Author: Warren Chen <[email protected]>
AuthorDate: Tue Mar 14 18:20:04 2023 +0800
refactor(framework): use generic to optimize (#4659)
---
backend/helpers/pluginhelper/api/scope_helper.go | 214 +++++++++------------
.../helpers/pluginhelper/api/scope_helper_test.go | 112 ++++++-----
backend/plugins/github/api/init.go | 5 +-
backend/plugins/github/api/scope.go | 37 +---
backend/plugins/github/models/repo.go | 4 +-
backend/plugins/gitlab/api/init.go | 5 +-
backend/plugins/gitlab/api/scope.go | 42 +---
7 files changed, 184 insertions(+), 235 deletions(-)
diff --git a/backend/helpers/pluginhelper/api/scope_helper.go
b/backend/helpers/pluginhelper/api/scope_helper.go
index 0901f69d8..962002a69 100644
--- a/backend/helpers/pluginhelper/api/scope_helper.go
+++ b/backend/helpers/pluginhelper/api/scope_helper.go
@@ -35,7 +35,7 @@ import (
)
// ScopeApiHelper is used to write the CURD of connection
-type ScopeApiHelper struct {
+type ScopeApiHelper[Conn any, Scope any, Tr any] struct {
log log.Logger
db dal.Dal
validator *validator.Validate
@@ -43,18 +43,18 @@ type ScopeApiHelper struct {
}
// NewScopeHelper creates a ScopeHelper for connection management
-func NewScopeHelper(
+func NewScopeHelper[Conn any, Scope any, Tr any](
basicRes context.BasicRes,
vld *validator.Validate,
connHelper *ConnectionApiHelper,
-) *ScopeApiHelper {
+) *ScopeApiHelper[Conn, Scope, Tr] {
if vld == nil {
vld = validator.New()
}
if connHelper == nil {
return nil
}
- return &ScopeApiHelper{
+ return &ScopeApiHelper[Conn, Scope, Tr]{
log: basicRes.GetLogger(),
db: basicRes.GetDal(),
validator: vld,
@@ -62,178 +62,169 @@ func NewScopeHelper(
}
}
+type ScopeRes[T any] struct {
+ Scope T
+ TransformationRuleName string `json:"transformationRuleName,omitempty"`
+}
+
+type ScopeReq[T any] struct {
+ Data []*T `json:"data"`
+}
+
// Put saves the given scopes to the database. It expects a slice of struct
pointers
// as the scopes argument. It also expects a fieldName argument, which is used
to extract
// the connection ID from the input.Params map.
-func (c *ScopeApiHelper) Put(input *plugin.ApiResourceInput, apiScope
interface{}, connection interface{}) errors.Error {
- err := errors.Convert(mapstructure.Decode(input.Body, apiScope))
- if err != nil {
- return errors.BadInput.Wrap(err, "decoding Github repo error")
+func (c *ScopeApiHelper[Conn, Scope, Tr]) Put(input *plugin.ApiResourceInput)
([]*Scope, errors.Error) {
+ var req struct {
+ Data []*Scope `json:"data"`
}
- // Ensure that the scopes argument is a slice
- v := reflect.ValueOf(apiScope)
- scopesValue := v.Elem().FieldByName("Data")
- if scopesValue.Kind() != reflect.Slice {
- panic("expected a slice")
+ err := errors.Convert(mapstructure.Decode(input.Body, &req))
+ if err != nil {
+ return nil, errors.BadInput.Wrap(err, "decoding Github repo
error")
}
// Extract the connection ID from the input.Params map
- connectionId, _ := ExtractParam(input.Params)
+ connectionId, _ := extractFromReqParam(input.Params)
if connectionId == 0 {
- return errors.BadInput.New("invalid connectionId or scopeId")
+ return nil, errors.BadInput.New("invalid connectionId or
scopeId")
}
- err = c.VerifyConnection(connection, connectionId)
+ err = c.VerifyConnection(connectionId)
if err != nil {
- return err
+ return nil, err
}
// Create a map to keep track of primary key values
keeper := make(map[string]struct{})
// Set the CreatedDate and UpdatedDate fields to the current time for
each scope
now := time.Now()
- for i := 0; i < scopesValue.Len(); i++ {
- // Get the reflect.Value of the i-th struct pointer in the slice
- structValue := scopesValue.Index(i)
-
- // Ensure that the structValue is a pointer to a struct
- if structValue.Kind() != reflect.Ptr ||
structValue.Elem().Kind() != reflect.Struct {
- panic("expected a pointer to a struct")
- }
-
+ for _, v := range req.Data {
// Ensure that the primary key value is unique
- primaryValueStr :=
ReturnPrimaryKeyValue(structValue.Elem().Interface())
+ primaryValueStr := returnPrimaryKeyValue(*v)
if _, ok := keeper[primaryValueStr]; ok {
- return errors.BadInput.New("duplicated item")
+ return nil, errors.BadInput.New("duplicated item")
} else {
keeper[primaryValueStr] = struct{}{}
}
// Set the connection ID, CreatedDate, and UpdatedDate fields
- SetScopeFields(structValue.Interface(), connectionId, &now,
&now)
+ setScopeFields(v, connectionId, &now, &now)
// Verify that the primary key value is valid
- err = VerifyPrimaryKeyValue(structValue.Elem().Interface())
+ err = VerifyScope(v, c.validator)
if err != nil {
- return err
+ return nil, err
}
}
// Save the scopes to the database
- return c.save(scopesValue.Interface(), c.db.Create)
+ return req.Data, c.save(&req.Data)
}
-func (c *ScopeApiHelper) Update(input *plugin.ApiResourceInput, fieldName
string, connection interface{}, scope interface{}) errors.Error {
- connectionId, scopeId := ExtractParam(input.Params)
+func (c *ScopeApiHelper[Conn, Scope, Tr]) Update(input
*plugin.ApiResourceInput, fieldName string) (*Scope, errors.Error) {
+ connectionId, scopeId := extractFromReqParam(input.Params)
if connectionId == 0 || len(scopeId) == 0 || scopeId == "0" {
- return errors.BadInput.New("invalid connectionId")
+ return nil, errors.BadInput.New("invalid connectionId")
}
- err := c.VerifyConnection(connection, connectionId)
+ err := c.VerifyConnection(connectionId)
if err != nil {
- return err
+ return nil, err
}
-
- err = c.db.First(scope, dal.Where(fmt.Sprintf("connection_id = ? AND %s
= ?", fieldName), connectionId, scopeId))
+ var scope Scope
+ err = c.db.First(&scope, dal.Where(fmt.Sprintf("connection_id = ? AND
%s = ?", fieldName), connectionId, scopeId))
if err != nil {
- return errors.Default.New("getting Scope error")
+ return nil, errors.Default.New("getting Scope error")
}
- err = DecodeMapStruct(input.Body, scope)
+ err = DecodeMapStruct(input.Body, &scope)
if err != nil {
- return errors.Default.Wrap(err, "patch scope error")
+ return nil, errors.Default.Wrap(err, "patch scope error")
}
- err = VerifyPrimaryKeyValue(scope)
+ err = VerifyScope(&scope, c.validator)
if err != nil {
- return err
+ return nil, errors.Default.Wrap(err, "Invalid scope")
}
+
err = c.db.Update(scope)
if err != nil {
- return errors.Default.Wrap(err, "error on saving Scope")
+ return nil, errors.Default.Wrap(err, "error on saving Scope")
}
- return nil
+ return &scope, nil
}
-func (c *ScopeApiHelper) GetScopeList(input *plugin.ApiResourceInput,
connection interface{}, scopes interface{}, rules interface{})
(map[uint64]string, errors.Error) {
- connectionId, _ := ExtractParam(input.Params)
+func (c *ScopeApiHelper[Conn, Scope, Tr]) GetScopeList(input
*plugin.ApiResourceInput) ([]ScopeRes[Scope], errors.Error) {
+ connectionId, _ := extractFromReqParam(input.Params)
if connectionId == 0 {
return nil, errors.BadInput.New("invalid path params")
}
- err := c.VerifyConnection(connection, connectionId)
+ err := c.VerifyConnection(connectionId)
if err != nil {
return nil, err
}
limit, offset := GetLimitOffset(input.Query, "pageSize", "page")
- err = c.db.All(scopes, dal.Where("connection_id = ?", connectionId),
dal.Limit(limit), dal.Offset(offset))
+ var scopes []*Scope
+ err = c.db.All(&scopes, dal.Where("connection_id = ?", connectionId),
dal.Limit(limit), dal.Offset(offset))
if err != nil {
return nil, err
}
- scopesValue :=
reflect.ValueOf(reflect.ValueOf(scopes).Elem().Interface())
- if scopesValue.Kind() != reflect.Slice {
- panic("expected a slice")
- }
var ruleIds []uint64
- for i := 0; i < scopesValue.Len(); i++ {
- // Get the reflect.Value of the i-th struct pointer in the slice
- structValue := scopesValue.Index(i)
-
- // Ensure that the structValue is a pointer to a struct
- if structValue.Kind() != reflect.Ptr ||
structValue.Elem().Kind() != reflect.Struct {
- panic("expected a pointer to a struct")
- }
- ruleId :=
structValue.Elem().FieldByName("TransformationRuleId").Uint()
+ for _, structValue := range scopes {
+ ruleId :=
reflect.ValueOf(structValue).Elem().FieldByName("TransformationRuleId").Uint()
if ruleId > 0 {
ruleIds = append(ruleIds, ruleId)
}
}
-
+ var rules []*Tr
if len(ruleIds) > 0 {
- err = c.db.All(rules, dal.Where("id IN (?)", ruleIds))
+ err = c.db.All(&rules, dal.Where("id IN (?)", ruleIds))
if err != nil {
return nil, err
}
}
- rulesValue := reflect.ValueOf(reflect.ValueOf(rules).Elem().Interface())
- if scopesValue.Kind() != reflect.Slice {
- panic("expected a slice")
- }
names := make(map[uint64]string)
- for i := 0; i < rulesValue.Len(); i++ {
+ for _, rule := range rules {
// Get the reflect.Value of the i-th struct pointer in the slice
- structValue := rulesValue.Index(i)
- names[structValue.FieldByName("ID").Uint()] =
structValue.FieldByName("Name").String()
+ names[reflect.ValueOf(rule).Elem().FieldByName("ID").Uint()] =
reflect.ValueOf(rule).Elem().FieldByName("Name").String()
}
- return names, nil
+ apiScopes := make([]ScopeRes[Scope], 0)
+ for _, scope := range scopes {
+ apiScopes = append(apiScopes, ScopeRes[Scope]{Scope: *scope,
TransformationRuleName:
names[reflect.ValueOf(scope).Elem().FieldByName("TransformationRuleId").Uint()]})
+ }
+ return apiScopes, nil
}
-func (c *ScopeApiHelper) GetScope(input *plugin.ApiResourceInput, fieldName
string, connection interface{}, scope interface{}, rule interface{})
errors.Error {
- connectionId, scopeId := ExtractParam(input.Params)
+func (c *ScopeApiHelper[Conn, Scope, Tr]) GetScope(input
*plugin.ApiResourceInput, fieldName string) (*ScopeRes[Scope], errors.Error) {
+ connectionId, scopeId := extractFromReqParam(input.Params)
if connectionId == 0 || len(scopeId) == 0 || scopeId == "0" {
- return errors.BadInput.New("invalid path params")
+ return nil, errors.BadInput.New("invalid path params")
}
- err := c.VerifyConnection(connection, connectionId)
+ err := c.VerifyConnection(connectionId)
if err != nil {
- return err
+ return nil, err
}
db := c.db
query := dal.Where(fmt.Sprintf("connection_id = ? AND %s = ?",
fieldName), connectionId, scopeId)
- err = db.First(scope, query)
+ var scope Scope
+ err = db.First(&scope, query)
if db.IsErrorNotFound(err) {
- return errors.NotFound.New("Scope not found")
+ return nil, errors.NotFound.New("Scope not found")
}
if err != nil {
- return err
+ return nil, err
}
- repoRuleId :=
reflect.ValueOf(scope).Elem().FieldByName("TransformationRuleId").Uint()
+ repoRuleId :=
reflect.ValueOf(scope).FieldByName("TransformationRuleId").Uint()
+ var rule Tr
if repoRuleId > 0 {
- err = db.First(rule, dal.Where("id = ?", repoRuleId))
+ err = db.First(&rule, dal.Where("id = ?", repoRuleId))
if err != nil {
- return errors.NotFound.New("transformationRule not
found")
+ return nil, errors.NotFound.New("transformationRule not
found")
}
}
- return nil
+ return &ScopeRes[Scope]{Scope: scope, TransformationRuleName:
reflect.ValueOf(rule).FieldByName("Name").String()}, nil
}
-func (c *ScopeApiHelper) VerifyConnection(connection interface{}, connId
uint64) errors.Error {
- err := c.connHelper.FirstById(&connection, connId)
+func (c *ScopeApiHelper[Conn, Scope, Tr]) VerifyConnection(connId uint64)
errors.Error {
+ var conn Conn
+ err := c.connHelper.FirstById(&conn, connId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.BadInput.New("Invalid Connection Id")
@@ -243,7 +234,7 @@ func (c *ScopeApiHelper) VerifyConnection(connection
interface{}, connId uint64)
return nil
}
-func (c *ScopeApiHelper) save(scope interface{}, method func(entity
interface{}, clauses ...dal.Clause) errors.Error) errors.Error {
+func (c *ScopeApiHelper[Conn, Scope, Tr]) save(scope interface{}) errors.Error
{
err := c.db.CreateOrUpdate(scope)
if err != nil {
if c.db.IsDuplicationError(err) {
@@ -254,7 +245,7 @@ func (c *ScopeApiHelper) save(scope interface{}, method
func(entity interface{},
return nil
}
-func ExtractParam(params map[string]string) (uint64, string) {
+func extractFromReqParam(params map[string]string) (uint64, string) {
connectionId, err := strconv.ParseUint(params["connectionId"], 10, 64)
if err != nil {
return 0, ""
@@ -263,33 +254,7 @@ func ExtractParam(params map[string]string) (uint64,
string) {
return connectionId, scopeId
}
-// VerifyPrimaryKeyValue function verifies that the primary key value of a
given struct instance is not zero or empty.
-func VerifyPrimaryKeyValue(i interface{}) errors.Error {
- var value reflect.Value
- pType := reflect.TypeOf(i)
- if pType.Kind() == reflect.Ptr {
- value = reflect.ValueOf(reflect.ValueOf(i).Elem().Interface())
- } else {
- value = reflect.ValueOf(i)
- }
- // Loop through the fields of the input struct using reflection
- for j := 0; j < value.NumField(); j++ {
- field := value.Field(j)
- tag := value.Type().Field(j).Tag.Get("gorm")
-
- // Check if the field is tagged as a primary key using the GORM
tag "primaryKey"
- if strings.Contains(tag, "primaryKey") {
- // If the field value is zero or nil, return an error
indicating that the primary key value is invalid
- if field.Interface() ==
reflect.Zero(field.Type()).Interface() || field.Interface() == nil {
- return errors.Default.New("primary key value is
zero or empty")
- }
- }
- }
- // If all primary key values are valid, return nil (no error)
- return nil
-}
-
-func SetScopeFields(p interface{}, connectionId uint64, createdDate
*time.Time, updatedDate *time.Time) {
+func setScopeFields(p interface{}, connectionId uint64, createdDate
*time.Time, updatedDate *time.Time) {
pType := reflect.TypeOf(p)
if pType.Kind() != reflect.Ptr {
panic("expected a pointer to a struct")
@@ -316,10 +281,10 @@ func SetScopeFields(p interface{}, connectionId uint64,
createdDate *time.Time,
}
}
-// ReturnPrimaryKeyValue returns a string containing the primary key value(s)
of a struct, concatenated with "-" between them.
+// returnPrimaryKeyValue returns a string containing the primary key value(s)
of a struct, concatenated with "-" between them.
// This function receives an interface{} type argument p, which can be a
pointer to any struct.
// The function uses reflection to iterate through the fields of the struct,
and checks if each field is tagged as "primaryKey".
-func ReturnPrimaryKeyValue(p interface{}) string {
+func returnPrimaryKeyValue(p interface{}) string {
result := ""
// get the type and value of the input interface using reflection
t := reflect.TypeOf(p)
@@ -344,3 +309,16 @@ func ReturnPrimaryKeyValue(p interface{}) string {
// return the final primary key value as a string
return result
}
+
+func VerifyScope(scope interface{}, vld *validator.Validate) errors.Error {
+ if vld != nil {
+ pType := reflect.TypeOf(scope)
+ if pType.Kind() != reflect.Ptr {
+ panic("expected a pointer to a struct")
+ }
+ if err := vld.Struct(scope); err != nil {
+ return errors.Default.Wrap(err, "error validating
target")
+ }
+ }
+ return nil
+}
diff --git a/backend/helpers/pluginhelper/api/scope_helper_test.go
b/backend/helpers/pluginhelper/api/scope_helper_test.go
index ad29684d9..a0e0e9afe 100644
--- a/backend/helpers/pluginhelper/api/scope_helper_test.go
+++ b/backend/helpers/pluginhelper/api/scope_helper_test.go
@@ -23,8 +23,10 @@ import (
"github.com/apache/incubator-devlake/helpers/unithelper"
mockcontext "github.com/apache/incubator-devlake/mocks/core/context"
mockdal "github.com/apache/incubator-devlake/mocks/core/dal"
+ "github.com/go-playground/validator/v10"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
+ "gorm.io/datatypes"
"gorm.io/gorm"
"reflect"
"testing"
@@ -32,7 +34,7 @@ import (
)
type TestModel struct {
- ID uint `gorm:"primaryKey"`
+ ID uint `gorm:"primaryKey" validate:"required"`
Name string `gorm:"primaryKey;type:BIGINT NOT NULL"`
}
@@ -70,11 +72,7 @@ func (GithubConnection) TableName() string {
return "_tool_github_connections"
}
-type req struct {
- Data []*GithubRepo `json:"data"`
-}
-
-func TestCheckPrimaryKeyValue(t *testing.T) {
+func TestVerifyScope(t *testing.T) {
testCases := []struct {
name string
model TestModel
@@ -102,12 +100,12 @@ func TestCheckPrimaryKeyValue(t *testing.T) {
ID: 1,
Name: "",
},
- wantErr: true,
+ wantErr: false,
},
}
for _, tc := range testCases {
- err := VerifyPrimaryKeyValue(tc.model)
+ err := VerifyScope(&tc.model, validator.New())
if (err != nil) != tc.wantErr {
t.Errorf("unexpected error value - got: %v, want: %v",
err, tc.wantErr)
}
@@ -115,6 +113,27 @@ func TestCheckPrimaryKeyValue(t *testing.T) {
}
}
+type GithubTransformationRule struct {
+ common.Model `mapstructure:"-"`
+ Name string `mapstructure:"name" json:"name"
gorm:"type:varchar(255);index:idx_name_github,unique" validate:"required"`
+ PrType string `mapstructure:"prType,omitempty"
json:"prType" gorm:"type:varchar(255)"`
+ PrComponent string
`mapstructure:"prComponent,omitempty" json:"prComponent"
gorm:"type:varchar(255)"`
+ PrBodyClosePattern string
`mapstructure:"prBodyClosePattern,omitempty" json:"prBodyClosePattern"
gorm:"type:varchar(255)"`
+ IssueSeverity string
`mapstructure:"issueSeverity,omitempty" json:"issueSeverity"
gorm:"type:varchar(255)"`
+ IssuePriority string
`mapstructure:"issuePriority,omitempty" json:"issuePriority"
gorm:"type:varchar(255)"`
+ IssueComponent string
`mapstructure:"issueComponent,omitempty" json:"issueComponent"
gorm:"type:varchar(255)"`
+ IssueTypeBug string
`mapstructure:"issueTypeBug,omitempty" json:"issueTypeBug"
gorm:"type:varchar(255)"`
+ IssueTypeIncident string
`mapstructure:"issueTypeIncident,omitempty" json:"issueTypeIncident"
gorm:"type:varchar(255)"`
+ IssueTypeRequirement string
`mapstructure:"issueTypeRequirement,omitempty" json:"issueTypeRequirement"
gorm:"type:varchar(255)"`
+ DeploymentPattern string
`mapstructure:"deploymentPattern,omitempty" json:"deploymentPattern"
gorm:"type:varchar(255)"`
+ ProductionPattern string
`mapstructure:"productionPattern,omitempty" json:"productionPattern"
gorm:"type:varchar(255)"`
+ Refdiff datatypes.JSONMap
`mapstructure:"refdiff,omitempty" json:"refdiff" swaggertype:"object"
format:"json"`
+}
+
+func (GithubTransformationRule) TableName() string {
+ return "_tool_github_transformation_rules"
+}
+
func TestSetGitlabProjectFields(t *testing.T) {
// create a struct
var p struct {
@@ -126,11 +145,11 @@ func TestSetGitlabProjectFields(t *testing.T) {
common.NoPKModel `json:"-" mapstructure:"-"`
}
- // call SetScopeFields to assign value
+ // call setScopeFields to assign value
connectionId := uint64(123)
createdDate := time.Now()
updatedDate := &createdDate
- SetScopeFields(&p, connectionId, &createdDate, updatedDate)
+ setScopeFields(&p, connectionId, &createdDate, updatedDate)
// verify fields
if p.ConnectionId != connectionId {
@@ -147,7 +166,7 @@ func TestSetGitlabProjectFields(t *testing.T) {
t.Errorf("UpdatedDate not set correctly, expected: %v, got:
%v", updatedDate, p.UpdatedDate)
}
- SetScopeFields(&p, connectionId, &createdDate, nil)
+ setScopeFields(&p, connectionId, &createdDate, nil)
// verify fields
if p.ConnectionId != connectionId {
@@ -183,10 +202,10 @@ func TestReturnPrimaryKeyValue(t *testing.T) {
}
// Call the function and check if it returns the correct primary key
value.
- result := ReturnPrimaryKeyValue(test)
+ result := returnPrimaryKeyValue(test)
expected := "1-123"
if result != expected {
- t.Errorf("ReturnPrimaryKeyValue returned %s, expected %s",
result, expected)
+ t.Errorf("returnPrimaryKeyValue returned %s, expected %s",
result, expected)
}
// Test with a different struct that has no field with primaryKey tag.
@@ -203,10 +222,10 @@ func TestReturnPrimaryKeyValue(t *testing.T) {
CreatedAt: time.Now(),
}
- result2 := ReturnPrimaryKeyValue(test2)
+ result2 := returnPrimaryKeyValue(test2)
expected2 := ""
if result2 != expected2 {
- t.Errorf("ReturnPrimaryKeyValue returned %s, expected %s",
result2, expected2)
+ t.Errorf("returnPrimaryKeyValue returned %s, expected %s",
result2, expected2)
}
}
@@ -233,37 +252,42 @@ func TestScopeApiHelper_Put(t *testing.T) {
connHelper := NewConnectionHelper(mockRes, nil)
// create a mock input, scopes, and connection
- input := &plugin.ApiResourceInput{Params:
map[string]string{"connectionId": "123"}}
- scopes := []*GithubRepo{
- {GithubId: 1, Name: "scope1"},
- {GithubId: 2, Name: "scope2"},
- }
- connection := &GithubConnection{}
- connection.ID = 3
- connection.Name = "test"
+ input := &plugin.ApiResourceInput{Params:
map[string]string{"connectionId": "123"}, Body: map[string]interface{}{
+ "data": []map[string]interface{}{
+ {
+ "HTMLUrl": "string",
+ "githubId": 1,
+ "cloneUrl": "string",
+ "connectionId": 1,
+ "createdAt": "string",
+ "createdDate": "string",
+ "description": "string",
+ "language": "string",
+ "name": "string",
+ "owner": "string",
+ "transformationRuleId": 0,
+ "updatedAt": "string",
+ "updatedDate": "string",
+ },
+ {
+ "HTMLUrl": "11",
+ "githubId": 2,
+ "cloneUrl": "string",
+ "connectionId": 1,
+ "createdAt": "string",
+ "createdDate": "string",
+ "description": "string",
+ "language": "string",
+ "name": "string",
+ "owner": "string",
+ "transformationRuleId": 0,
+ "updatedAt": "string",
+ "updatedDate": "string",
+ }}}}
// create a mock ScopeApiHelper with a mock database connection
- apiHelper := &ScopeApiHelper{db: mockDal, connHelper: connHelper}
- apiReq := req{Data: scopes}
+ apiHelper := &ScopeApiHelper[GithubConnection, GithubRepo,
GithubTransformationRule]{db: mockDal, connHelper: connHelper}
// test a successful call to Put
- err := apiHelper.Put(input, &apiReq, connection)
+ _, err := apiHelper.Put(input)
assert.NoError(t, err)
-
- // test a call to Put with a duplicate primary key value
- duplicateScopes := []*GithubRepo{
- {GithubId: 1, Name: "scope1"},
- {GithubId: 1, Name: "scope2"},
- }
- apiReq.Data = duplicateScopes
- err = apiHelper.Put(input, &apiReq, connection)
- assert.Error(t, err)
-
- // test a call to Put with an invalid primary key value
- invalidScopes := []*GithubRepo{
- {GithubId: 0, Name: "scope1"},
- }
- apiReq.Data = invalidScopes
-
- err = apiHelper.Put(input, &apiReq, connection)
- assert.Error(t, err)
}
diff --git a/backend/plugins/github/api/init.go
b/backend/plugins/github/api/init.go
index 8f0dc3bad..3044a56ba 100644
--- a/backend/plugins/github/api/init.go
+++ b/backend/plugins/github/api/init.go
@@ -20,12 +20,13 @@ package api
import (
"github.com/apache/incubator-devlake/core/context"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/github/models"
"github.com/go-playground/validator/v10"
)
var vld *validator.Validate
var connectionHelper *api.ConnectionApiHelper
-var scopeHelper *api.ScopeApiHelper
+var scopeHelper *api.ScopeApiHelper[models.GithubConnection,
models.GithubRepo, models.GithubTransformationRule]
var basicRes context.BasicRes
func Init(br context.BasicRes) {
@@ -35,7 +36,7 @@ func Init(br context.BasicRes) {
basicRes,
vld,
)
- scopeHelper = api.NewScopeHelper(
+ scopeHelper = api.NewScopeHelper[models.GithubConnection,
models.GithubRepo, models.GithubTransformationRule](
basicRes,
vld,
connectionHelper,
diff --git a/backend/plugins/github/api/scope.go
b/backend/plugins/github/api/scope.go
index 245d6eb39..e4b2d4bfe 100644
--- a/backend/plugins/github/api/scope.go
+++ b/backend/plugins/github/api/scope.go
@@ -20,19 +20,9 @@ package api
import (
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/plugin"
- "github.com/apache/incubator-devlake/plugins/github/models"
"net/http"
)
-type apiRepo struct {
- models.GithubRepo
- TransformationRuleName string `json:"transformationRuleName,omitempty"`
-}
-
-type req struct {
- Data []*models.GithubRepo `json:"data"`
-}
-
// PutScope create or update github repo
// @Summary create or update github repo
// @Description Create or update github repo
@@ -45,12 +35,11 @@ type req struct {
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/github/connections/{connectionId}/scopes [PUT]
func PutScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
errors.Error) {
- var repos req
- err := scopeHelper.Put(input, &repos, &models.GithubConnection{})
+ repos, err := scopeHelper.Put(input)
if err != nil {
return nil, errors.Default.Wrap(err, "error on saving
GithubRepo")
}
- return &plugin.ApiResourceOutput{Body: repos.Data, Status:
http.StatusOK}, nil
+ return &plugin.ApiResourceOutput{Body: repos, Status: http.StatusOK},
nil
}
// UpdateScope patch to github repo
@@ -66,9 +55,7 @@ func PutScope(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/github/connections/{connectionId}/scopes/{scopeId} [PATCH]
func UpdateScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
errors.Error) {
- var repo models.GithubRepo
- var conn models.GithubConnection
- err := scopeHelper.Update(input, "github_id", &conn, &repo)
+ repo, err := scopeHelper.Update(input, "github_id")
if err != nil {
return &plugin.ApiResourceOutput{Body: nil, Status:
http.StatusInternalServerError}, err
}
@@ -87,18 +74,11 @@ func UpdateScope(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, err
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/github/connections/{connectionId}/scopes/ [GET]
func GetScopeList(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
errors.Error) {
- var repos []*models.GithubRepo
- var rules []*models.GithubTransformationRule
- var conn models.GithubConnection
- names, err := scopeHelper.GetScopeList(input, conn, &repos, &rules)
+ apiScopes, err := scopeHelper.GetScopeList(input)
if err != nil {
return nil, err
}
- var apiRepos []apiRepo
- for _, repo := range repos {
- apiRepos = append(apiRepos, apiRepo{*repo,
names[repo.TransformationRuleId]})
- }
- return &plugin.ApiResourceOutput{Body: apiRepos, Status:
http.StatusOK}, nil
+ return &plugin.ApiResourceOutput{Body: apiScopes, Status:
http.StatusOK}, nil
}
// GetScope get one Github repo
@@ -112,12 +92,9 @@ func GetScopeList(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, er
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/github/connections/{connectionId}/scopes/{id} [GET]
func GetScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
errors.Error) {
- var repo models.GithubRepo
- var rule models.GithubTransformationRule
- var conn models.GithubConnection
- err := scopeHelper.GetScope(input, "github_id", conn, &repo, &rule)
+ apiScope, err := scopeHelper.GetScope(input, "github_id")
if err != nil {
return nil, err
}
- return &plugin.ApiResourceOutput{Body: apiRepo{repo, rule.Name},
Status: http.StatusOK}, nil
+ return &plugin.ApiResourceOutput{Body: apiScope, Status:
http.StatusOK}, nil
}
diff --git a/backend/plugins/github/models/repo.go
b/backend/plugins/github/models/repo.go
index de215b15a..9eda5e118 100644
--- a/backend/plugins/github/models/repo.go
+++ b/backend/plugins/github/models/repo.go
@@ -23,8 +23,8 @@ import (
)
type GithubRepo struct {
- ConnectionId uint64 `json:"connectionId" gorm:"primaryKey"
mapstructure:"connectionId,omitempty"`
- GithubId int `json:"githubId" gorm:"primaryKey"
mapstructure:"githubId"`
+ ConnectionId uint64 `json:"connectionId" gorm:"primaryKey"
validate:"required" mapstructure:"connectionId,omitempty"`
+ GithubId int `json:"githubId" gorm:"primaryKey"
validate:"required" mapstructure:"githubId"`
Name string `json:"name" gorm:"type:varchar(255)"
mapstructure:"name,omitempty"`
HTMLUrl string `json:"HTMLUrl"
gorm:"type:varchar(255)" mapstructure:"HTMLUrl,omitempty"`
Description string `json:"description"
mapstructure:"description,omitempty"`
diff --git a/backend/plugins/gitlab/api/init.go
b/backend/plugins/gitlab/api/init.go
index 8f0dc3bad..b4190c90b 100644
--- a/backend/plugins/gitlab/api/init.go
+++ b/backend/plugins/gitlab/api/init.go
@@ -20,12 +20,13 @@ package api
import (
"github.com/apache/incubator-devlake/core/context"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/gitlab/models"
"github.com/go-playground/validator/v10"
)
var vld *validator.Validate
var connectionHelper *api.ConnectionApiHelper
-var scopeHelper *api.ScopeApiHelper
+var scopeHelper *api.ScopeApiHelper[models.GitlabConnection,
models.GitlabProject, models.GitlabTransformationRule]
var basicRes context.BasicRes
func Init(br context.BasicRes) {
@@ -35,7 +36,7 @@ func Init(br context.BasicRes) {
basicRes,
vld,
)
- scopeHelper = api.NewScopeHelper(
+ scopeHelper = api.NewScopeHelper[models.GitlabConnection,
models.GitlabProject, models.GitlabTransformationRule](
basicRes,
vld,
connectionHelper,
diff --git a/backend/plugins/gitlab/api/scope.go
b/backend/plugins/gitlab/api/scope.go
index 4e660b9a2..e41c729ab 100644
--- a/backend/plugins/gitlab/api/scope.go
+++ b/backend/plugins/gitlab/api/scope.go
@@ -32,10 +32,6 @@ type apiProject struct {
TransformationRuleName string `json:"transformationRuleName,omitempty"`
}
-type req struct {
- Data []*models.GitlabProject `json:"data"`
-}
-
// PutScope create or update gitlab project
// @Summary create or update gitlab project
// @Description Create or update gitlab project
@@ -48,12 +44,11 @@ type req struct {
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/gitlab/connections/{connectionId}/scopes [PUT]
func PutScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
errors.Error) {
- var scopes req
- err := scopeHelper.Put(input, &scopes, &models.GitlabConnection{})
+ repos, err := scopeHelper.Put(input)
if err != nil {
return nil, errors.Default.Wrap(err, "error on saving
GithubRepo")
}
- return &plugin.ApiResourceOutput{Body: scopes.Data, Status:
http.StatusOK}, nil
+ return &plugin.ApiResourceOutput{Body: repos, Status: http.StatusOK},
nil
}
// UpdateScope patch to gitlab project
@@ -69,28 +64,11 @@ func PutScope(input *plugin.ApiResourceInput)
(*plugin.ApiResourceOutput, errors
// @Failure 500 {object} shared.ApiBody "Internal Error"
// @Router /plugins/gitlab/connections/{connectionId}/scopes/{projectId}
[PATCH]
func UpdateScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput,
errors.Error) {
- connectionId, projectId := extractParam(input.Params)
- if connectionId*projectId == 0 {
- return nil, errors.BadInput.New("invalid connectionId or
projectId")
- }
- var project models.GitlabProject
- err := basicRes.GetDal().First(&project, dal.Where("connection_id = ?
AND gitlab_id = ?", connectionId, projectId))
- if err != nil {
- return nil, errors.Default.Wrap(err, "getting GitlabProject
error")
- }
- err = api.DecodeMapStruct(input.Body, &project)
+ repo, err := scopeHelper.Update(input, "gitlab_id")
if err != nil {
- return nil, errors.Default.Wrap(err, "patch gitlab project
error")
+ return &plugin.ApiResourceOutput{Body: nil, Status:
http.StatusInternalServerError}, err
}
- err = verifyProject(&project)
- if err != nil {
- return nil, err
- }
- err = basicRes.GetDal().Update(project)
- if err != nil {
- return nil, errors.Default.Wrap(err, "error on saving
GitlabProject")
- }
- return &plugin.ApiResourceOutput{Body: project, Status: http.StatusOK},
nil
+ return &plugin.ApiResourceOutput{Body: repo, Status: http.StatusOK}, nil
}
// GetScopeList get Gitlab projects
@@ -178,13 +156,3 @@ func extractParam(params map[string]string) (uint64,
uint64) {
projectId, _ := strconv.ParseUint(params["projectId"], 10, 64)
return connectionId, projectId
}
-
-func verifyProject(project *models.GitlabProject) errors.Error {
- if project.ConnectionId == 0 {
- return errors.BadInput.New("invalid connectionId")
- }
- if project.GitlabId <= 0 {
- return errors.BadInput.New("invalid projectId")
- }
- return nil
-}