You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by kl...@apache.org on 2022/06/20 14:34:34 UTC
[incubator-devlake] branch main updated: feat: multi-data connections support for ae (#2208)
This is an automated email from the ASF dual-hosted git repository.
klesh 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 7b64c9e8 feat: multi-data connections support for ae (#2208)
7b64c9e8 is described below
commit 7b64c9e8f7643d8c65a2bcfb497eedb9fc2e761e
Author: mappjzc <zh...@merico.dev>
AuthorDate: Mon Jun 20 22:34:28 2022 +0800
feat: multi-data connections support for ae (#2208)
* feat: multi-data connections support for ae
Add multi-data support for AE
Mv randomstr to core
Add TestConnection for AE
Nddtfjiang <zh...@merico.dev>
* refactor: change test api to projects
Change test api to projects from projects/
Move GetSign from api to models.
Remove Tasks of AeOptions.
Nddtfjiang <zh...@merico.dev>
* refactor: spread out aeconnection at migrationscripts
spread out aeconnection at migrationscripts
Nddtfjiang <zh...@merico.dev>
* fix: fix services init early than main
Change services init to Init and call it on main
Nddtfjiang <zh...@merico.dev>
* refactor: change connction test from param not db
Change connction test to use param not db.
Nddtfjiang <zh...@merico.dev>
* fix: fix e2e test lost init
Add init to e2e test TestNewTask
Nddtfjiang <zh...@merico.dev>
---
main.go | 2 +
plugins/ae/ae.go | 38 +++++-
plugins/ae/api/connection.go | 135 ++++++++++++++-------
plugins/ae/models/connection.go | 54 ++++++++-
.../{ => migrationscripts/archived}/connection.go | 29 +++--
.../migrationscripts/updateSchemas20220615.go} | 28 +++--
plugins/ae/tasks/api_client.go | 71 ++---------
plugins/ae/tasks/task_data.go | 4 +-
plugins/core/plugin_utils.go | 14 +++
plugins/helper/connection.go | 5 +
services/init.go | 2 +-
test/api/task/task_test.go | 27 +++--
12 files changed, 263 insertions(+), 146 deletions(-)
diff --git a/main.go b/main.go
index 7c3001f7..2ffe728d 100644
--- a/main.go
+++ b/main.go
@@ -21,6 +21,7 @@ import (
"github.com/apache/incubator-devlake/api"
"github.com/apache/incubator-devlake/config"
"github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/services"
_ "github.com/apache/incubator-devlake/version"
)
@@ -36,5 +37,6 @@ func main() {
panic(err)
}
}
+ services.Init()
api.CreateApiService()
}
diff --git a/plugins/ae/ae.go b/plugins/ae/ae.go
index e2bad02f..4ca20b24 100644
--- a/plugins/ae/ae.go
+++ b/plugins/ae/ae.go
@@ -22,9 +22,11 @@ import (
"github.com/apache/incubator-devlake/migration"
"github.com/apache/incubator-devlake/plugins/ae/api"
+ "github.com/apache/incubator-devlake/plugins/ae/models"
"github.com/apache/incubator-devlake/plugins/ae/models/migrationscripts"
"github.com/apache/incubator-devlake/plugins/ae/tasks"
"github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/plugins/helper"
"github.com/apache/incubator-devlake/runner"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cobra"
@@ -41,6 +43,7 @@ var _ core.Migratable = (*AE)(nil)
type AE struct{}
func (plugin AE) Init(config *viper.Viper, logger core.Logger, db *gorm.DB) error {
+ api.Init(config, logger, db)
return nil
}
@@ -67,10 +70,26 @@ func (plugin AE) PrepareTaskData(taskCtx core.TaskContext, options map[string]in
if op.ProjectId <= 0 {
return nil, fmt.Errorf("projectId is required")
}
- apiClient, err := tasks.CreateApiClient(taskCtx)
+
+ connection := &models.AeConnection{}
+ connectionHelper := helper.NewConnectionHelper(
+ taskCtx,
+ nil,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ err = connectionHelper.FirstById(connection, op.ConnectionId)
if err != nil {
return nil, err
}
+
+ apiClient, err := tasks.CreateApiClient(taskCtx, connection)
+ if err != nil {
+ return nil, err
+ }
+
return &tasks.AeTaskData{
Options: &op,
ApiClient: apiClient,
@@ -82,7 +101,10 @@ func (plugin AE) RootPkgPath() string {
}
func (plugin AE) MigrationScripts() []migration.Script {
- return []migration.Script{new(migrationscripts.InitSchemas)}
+ return []migration.Script{
+ new(migrationscripts.InitSchemas),
+ new(migrationscripts.UpdateSchemas20220615),
+ }
}
func (plugin AE) ApiResources() map[string]map[string]core.ApiResourceHandler {
@@ -91,11 +113,13 @@ func (plugin AE) ApiResources() map[string]map[string]core.ApiResourceHandler {
"GET": api.TestConnection,
},
"connections": {
- "GET": api.ListConnections,
+ "GET": api.ListConnections,
+ "POST": api.PostConnections,
},
"connections/:connectionId": {
- "GET": api.GetConnection,
- "PATCH": api.PatchConnection,
+ "GET": api.GetConnection,
+ "PATCH": api.PatchConnection,
+ "DELETE": api.DeleteConnection,
},
}
}
@@ -105,11 +129,13 @@ var PluginEntry AE //nolint
func main() {
aeCmd := &cobra.Command{Use: "ae"}
+ connectionId := aeCmd.Flags().Uint64P("Connection-id", "c", 0, "ae connection id")
projectId := aeCmd.Flags().IntP("project-id", "p", 0, "ae project id")
_ = aeCmd.MarkFlagRequired("project-id")
aeCmd.Run = func(cmd *cobra.Command, args []string) {
runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
- "projectId": *projectId,
+ "connectionId": *connectionId,
+ "projectId": *projectId,
})
}
runner.RunCmd(aeCmd)
diff --git a/plugins/ae/api/connection.go b/plugins/ae/api/connection.go
index c66f9e11..9dc2119b 100644
--- a/plugins/ae/api/connection.go
+++ b/plugins/ae/api/connection.go
@@ -18,88 +18,141 @@ limitations under the License.
package api
import (
- "github.com/apache/incubator-devlake/config"
+ "fmt"
+ "net/http"
+ "time"
+
"github.com/apache/incubator-devlake/plugins/ae/models"
"github.com/apache/incubator-devlake/plugins/core"
"github.com/apache/incubator-devlake/plugins/helper"
- "net/http"
+ "github.com/go-playground/validator/v10"
+ "github.com/mitchellh/mapstructure"
+ "github.com/spf13/viper"
+ "gorm.io/gorm"
)
type ApiMeResponse struct {
Name string `json:"name"`
}
-/*
-GET /plugins/ae/test
-*/
-func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
- // TODO: implement test connection
- return &core.ApiResourceOutput{Body: true}, nil
+var vld *validator.Validate
+var connectionHelper *helper.ConnectionApiHelper
+
+func Init(config *viper.Viper, logger core.Logger, database *gorm.DB) {
+ basicRes := helper.NewDefaultBasicRes(config, logger, database)
+ vld = validator.New()
+ connectionHelper = helper.NewConnectionHelper(
+ basicRes,
+ vld,
+ )
}
/*
-PATCH /plugins/ae/connections/:connectionId
+GET /plugins/ae/test/
*/
-func PatchConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
- v := config.GetConfig()
- connection := &models.AeConnection{}
- err := helper.EncodeStruct(v, connection, "env")
+func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ // decode
+ var err error
+ var connection models.TestConnectionRequest
+ err = mapstructure.Decode(input.Body, &connection)
if err != nil {
return nil, err
}
- // update from request and save to .env
- err = helper.DecodeStruct(v, connection, input.Body, "env")
+ // validate
+ err = vld.Struct(connection)
if err != nil {
return nil, err
}
- err = config.WriteConfig(v)
+
+ // load and process cconfiguration
+ endpoint := connection.Endpoint
+ appId := connection.AppId
+ secretKey := connection.SecretKey
+ proxy := connection.Proxy
+
+ apiClient, err := helper.NewApiClient(endpoint, nil, 3*time.Second, proxy, nil)
+ if err != nil {
+ return nil, err
+ }
+ apiClient.SetBeforeFunction(func(req *http.Request) error {
+ nonceStr := core.RandLetterBytes(8)
+ timestamp := fmt.Sprintf("%v", time.Now().Unix())
+ sign := models.GetSign(req.URL.Query(), appId, secretKey, nonceStr, timestamp)
+ req.Header.Set("x-ae-app-id", appId)
+ req.Header.Set("x-ae-timestamp", timestamp)
+ req.Header.Set("x-ae-nonce-str", nonceStr)
+ req.Header.Set("x-ae-sign", sign)
+ return nil
+ })
+ res, err := apiClient.Get("projects", nil, nil)
if err != nil {
return nil, err
}
- response := models.AeResponse{
- AeConnection: *connection,
- Name: "Ae",
- ID: 1,
+
+ switch res.StatusCode {
+ case 200: // right StatusCode
+ return &core.ApiResourceOutput{Body: true, Status: 200}, nil
+ case 401: // error secretKey or nonceStr
+ return &core.ApiResourceOutput{Body: false, Status: res.StatusCode}, nil
+ default: // unknow what happen , back to user
+ return &core.ApiResourceOutput{Body: res.Body, Status: res.StatusCode}, nil
}
- return &core.ApiResourceOutput{Body: response, Status: http.StatusOK}, nil
}
/*
-GET /plugins/ae/connections
+POST /plugins/ae/connections
*/
-func ListConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
- // RETURN ONLY 1 SOURCE (FROM ENV) until multi-connection is developed.
- v := config.GetConfig()
+func PostConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
connection := &models.AeConnection{}
-
- err := helper.EncodeStruct(v, connection, "env")
+ err := connectionHelper.Create(connection, input)
if err != nil {
return nil, err
}
- response := models.AeResponse{
- AeConnection: *connection,
- Name: "Ae",
- ID: 1,
- }
+ return &core.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil
+}
- return &core.ApiResourceOutput{Body: []models.AeResponse{response}}, nil
+/*
+GET /plugins/ae/connections
+*/
+func ListConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ var connections []models.AeConnection
+ err := connectionHelper.List(&connections)
+ if err != nil {
+ return nil, err
+ }
+ return &core.ApiResourceOutput{Body: connections, Status: http.StatusOK}, nil
}
/*
GET /plugins/ae/connections/:connectionId
*/
func GetConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
- // RETURN ONLY 1 SOURCE FROM ENV (Ignore ID until multi-connection is developed.)
- v := config.GetConfig()
connection := &models.AeConnection{}
- err := helper.EncodeStruct(v, connection, "env")
+ err := connectionHelper.First(connection, input.Params)
+ return &core.ApiResourceOutput{Body: connection}, err
+}
+
+/*
+PATCH /plugins/ae/connections/:connectionId
+*/
+func PatchConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ connection := &models.AeConnection{}
+ err := connectionHelper.Patch(connection, input)
if err != nil {
return nil, err
}
- response := &models.AeResponse{
- AeConnection: *connection,
- Name: "Ae",
- ID: 1,
+ return &core.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil
+}
+
+/*
+DELETE /plugins/ae/connections/:connectionId
+*/
+func DeleteConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
+ connection := &models.AeConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ if err != nil {
+ return nil, err
}
- return &core.ApiResourceOutput{Body: response}, nil
+ err = connectionHelper.Delete(connection)
+ return &core.ApiResourceOutput{Body: connection}, err
}
diff --git a/plugins/ae/models/connection.go b/plugins/ae/models/connection.go
index 99624b56..96ae9b1c 100644
--- a/plugins/ae/models/connection.go
+++ b/plugins/ae/models/connection.go
@@ -17,11 +17,26 @@ limitations under the License.
package models
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "fmt"
+ "net/url"
+ "sort"
+ "strings"
+
+ "github.com/apache/incubator-devlake/plugins/helper"
+)
+
type AeConnection struct {
- AppId string `mapstructure:"appId" env:"AE_APP_ID" json:"appId"`
- Sign string `mapstructure:"sign" env:"AE_SIGN" json:"sign"`
- NonceStr string `mapstructure:"nonceStr" env:"AE_NONCE_STR" json:"nonceStr"`
- Endpoint string `mapstructure:"endpoint" env:"AE_ENDPOINT" json:"endpoint"`
+ helper.RestConnection `mapstructure:",squash"`
+ helper.AppKey `mapstructure:",squash"`
+}
+
+type TestConnectionRequest struct {
+ Endpoint string `json:"endpoint"`
+ Proxy string `json:"proxy"`
+ helper.AppKey `mapstructure:",squash"`
}
// This object conforms to what the frontend currently expects.
@@ -30,3 +45,34 @@ type AeResponse struct {
Name string `json:"name"`
ID int `json:"id"`
}
+
+func (AeConnection) TableName() string {
+ return "_tool_ae_connections"
+}
+
+func GetSign(query url.Values, appId, secretKey, nonceStr, timestamp string) string {
+ // clone query because we need to add items
+ kvs := make([]string, 0, len(query)+3)
+ kvs = append(kvs, fmt.Sprintf("app_id=%s", appId))
+ kvs = append(kvs, fmt.Sprintf("timestamp=%s", timestamp))
+ kvs = append(kvs, fmt.Sprintf("nonce_str=%s", nonceStr))
+ for key, values := range query {
+ for _, value := range values {
+ kvs = append(kvs, fmt.Sprintf("%s=%s", url.QueryEscape(key), url.QueryEscape(value)))
+ }
+ }
+
+ // sort by alphabetical order
+ sort.Strings(kvs)
+
+ // generate text for signature
+ querystring := fmt.Sprintf("%s&key=%s", strings.Join(kvs, "&"), url.QueryEscape(secretKey))
+
+ // sign it
+ hasher := md5.New()
+ _, err := hasher.Write([]byte(querystring))
+ if err != nil {
+ return ""
+ }
+ return strings.ToUpper(hex.EncodeToString(hasher.Sum(nil)))
+}
diff --git a/plugins/ae/models/connection.go b/plugins/ae/models/migrationscripts/archived/connection.go
similarity index 52%
copy from plugins/ae/models/connection.go
copy to plugins/ae/models/migrationscripts/archived/connection.go
index 99624b56..6cbfe39e 100644
--- a/plugins/ae/models/connection.go
+++ b/plugins/ae/models/migrationscripts/archived/connection.go
@@ -15,18 +15,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package models
+package archived
+
+import (
+ "time"
+)
type AeConnection struct {
- AppId string `mapstructure:"appId" env:"AE_APP_ID" json:"appId"`
- Sign string `mapstructure:"sign" env:"AE_SIGN" json:"sign"`
- NonceStr string `mapstructure:"nonceStr" env:"AE_NONCE_STR" json:"nonceStr"`
- Endpoint string `mapstructure:"endpoint" env:"AE_ENDPOINT" json:"endpoint"`
+ Name string `gorm:"type:varchar(100);uniqueIndex" json:"name" validate:"required"`
+
+ ID uint64 `gorm:"primaryKey" json:"id"`
+ CreatedAt time.Time `json:"createdAt"`
+ UpdatedAt time.Time `json:"updatedAt"`
+
+ Endpoint string `mapstructure:"endpoint" validate:"required" json:"endpoint"`
+ Proxy string `mapstructure:"proxy" json:"proxy"`
+ RateLimit int `comment:"api request rate limit per hour" json:"rateLimit"`
+
+ AppId string `mapstructure:"app_id" validate:"required" json:"app_id"`
+ SecretKey string `mapstructure:"secret_key" validate:"required" json:"secret_key" encrypt:"yes"`
}
-// This object conforms to what the frontend currently expects.
-type AeResponse struct {
- AeConnection
- Name string `json:"name"`
- ID int `json:"id"`
+func (AeConnection) TableName() string {
+ return "_tool_ae_connections"
}
diff --git a/plugins/ae/tasks/task_data.go b/plugins/ae/models/migrationscripts/updateSchemas20220615.go
similarity index 60%
copy from plugins/ae/tasks/task_data.go
copy to plugins/ae/models/migrationscripts/updateSchemas20220615.go
index 1ea21557..07c604a0 100644
--- a/plugins/ae/tasks/task_data.go
+++ b/plugins/ae/models/migrationscripts/updateSchemas20220615.go
@@ -15,19 +15,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package tasks
+package migrationscripts
-import "github.com/apache/incubator-devlake/plugins/helper"
+import (
+ "context"
-type AeOptions struct {
- ProjectId int
- Tasks []string `json:"tasks,omitempty"`
+ "github.com/apache/incubator-devlake/plugins/ae/models/migrationscripts/archived"
+ "gorm.io/gorm"
+)
+
+type UpdateSchemas20220615 struct{}
+
+func (*UpdateSchemas20220615) Up(ctx context.Context, db *gorm.DB) error {
+ return db.Migrator().AutoMigrate(
+ &archived.AeConnection{},
+ )
}
-type AeTaskData struct {
- Options *AeOptions
- ApiClient *helper.ApiAsyncClient
+func (*UpdateSchemas20220615) Version() uint64 {
+ return 20220615181010
}
-type AeApiParams struct {
- ProjectId int
+
+func (*UpdateSchemas20220615) Name() string {
+ return "create tables:" + archived.AeConnection{}.TableName()
}
diff --git a/plugins/ae/tasks/api_client.go b/plugins/ae/tasks/api_client.go
index da38e2c1..4ce1642e 100644
--- a/plugins/ae/tasks/api_client.go
+++ b/plugins/ae/tasks/api_client.go
@@ -18,85 +18,30 @@ limitations under the License.
package tasks
import (
- "crypto/md5"
- "encoding/hex"
"fmt"
- "math/rand"
"net/http"
- "net/url"
- "sort"
- "strings"
"time"
+ "github.com/apache/incubator-devlake/plugins/ae/models"
"github.com/apache/incubator-devlake/plugins/core"
"github.com/apache/incubator-devlake/plugins/helper"
)
-const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-
-func init() {
- rand.Seed(time.Now().UnixNano())
-}
-
-func RandString(n int) string {
- b := make([]byte, n)
- for i := range b {
- b[i] = letterBytes[rand.Intn(len(letterBytes))]
- }
- return string(b)
-}
-
-func getSign(query url.Values, appId, secretKey, nonceStr, timestamp string) string {
- // clone query because we need to add items
- kvs := make([]string, 0, len(query)+3)
- kvs = append(kvs, fmt.Sprintf("app_id=%s", appId))
- kvs = append(kvs, fmt.Sprintf("timestamp=%s", timestamp))
- kvs = append(kvs, fmt.Sprintf("nonce_str=%s", nonceStr))
- for key, values := range query {
- for _, value := range values {
- kvs = append(kvs, fmt.Sprintf("%s=%s", url.QueryEscape(key), url.QueryEscape(value)))
- }
- }
-
- // sort by alphabetical order
- sort.Strings(kvs)
-
- // generate text for signature
- querystring := fmt.Sprintf("%s&key=%s", strings.Join(kvs, "&"), url.QueryEscape(secretKey))
-
- // sign it
- hasher := md5.New()
- _, err := hasher.Write([]byte(querystring))
- if err != nil {
- return ""
- }
- return strings.ToUpper(hex.EncodeToString(hasher.Sum(nil)))
-}
-
-func CreateApiClient(taskCtx core.TaskContext) (*helper.ApiAsyncClient, error) {
+func CreateApiClient(taskCtx core.TaskContext, connection *models.AeConnection) (*helper.ApiAsyncClient, error) {
// load and process cconfiguration
- endpoint := taskCtx.GetConfig("AE_ENDPOINT")
- if endpoint == "" {
- return nil, fmt.Errorf("invalid AE_ENDPOINT")
- }
- appId := taskCtx.GetConfig("AE_APP_ID")
- if appId == "" {
- return nil, fmt.Errorf("invalid AE_APP_ID")
- }
- secretKey := taskCtx.GetConfig("AE_SECRET_KEY")
- if secretKey == "" {
- return nil, fmt.Errorf("invalid AE_SECRET_KEY")
- }
- proxy := taskCtx.GetConfig("AE_PROXY")
+ endpoint := connection.Endpoint
+ appId := connection.AppId
+ secretKey := connection.SecretKey
+ proxy := connection.Proxy
apiClient, err := helper.NewApiClient(endpoint, nil, 0, proxy, taskCtx.GetContext())
if err != nil {
return nil, err
}
apiClient.SetBeforeFunction(func(req *http.Request) error {
- nonceStr := RandString(8)
+ nonceStr := core.RandLetterBytes(8)
timestamp := fmt.Sprintf("%v", time.Now().Unix())
- sign := getSign(req.URL.Query(), appId, secretKey, nonceStr, timestamp)
+ sign := models.GetSign(req.URL.Query(), appId, secretKey, nonceStr, timestamp)
req.Header.Set("x-ae-app-id", appId)
req.Header.Set("x-ae-timestamp", timestamp)
req.Header.Set("x-ae-nonce-str", nonceStr)
diff --git a/plugins/ae/tasks/task_data.go b/plugins/ae/tasks/task_data.go
index 1ea21557..0bff2af5 100644
--- a/plugins/ae/tasks/task_data.go
+++ b/plugins/ae/tasks/task_data.go
@@ -20,8 +20,8 @@ package tasks
import "github.com/apache/incubator-devlake/plugins/helper"
type AeOptions struct {
- ProjectId int
- Tasks []string `json:"tasks,omitempty"`
+ ConnectionId uint64 `json:"connectionId"`
+ ProjectId int
}
type AeTaskData struct {
diff --git a/plugins/core/plugin_utils.go b/plugins/core/plugin_utils.go
index 95df35b6..b3f64380 100644
--- a/plugins/core/plugin_utils.go
+++ b/plugins/core/plugin_utils.go
@@ -30,6 +30,10 @@ import (
const EncodeKeyEnvStr = "ENCODE_KEY"
+func init() {
+ rand.Seed(time.Now().UnixNano())
+}
+
// TODO: maybe move encryption/decryption into helper?
// AES + Base64 encryption using ENCODE_KEY in .env as key
func Encrypt(encKey, plainText string) (string, error) {
@@ -152,3 +156,13 @@ func RandomCapsStr(len int) string {
func RandomEncKey() string {
return RandomCapsStr(128)
}
+
+const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+
+func RandLetterBytes(n int) string {
+ b := make([]byte, n)
+ for i := range b {
+ b[i] = letterBytes[rand.Intn(len(letterBytes))]
+ }
+ return string(b)
+}
diff --git a/plugins/helper/connection.go b/plugins/helper/connection.go
index f0c3f8d8..14d66a65 100644
--- a/plugins/helper/connection.go
+++ b/plugins/helper/connection.go
@@ -49,6 +49,11 @@ type AccessToken struct {
Token string `mapstructure:"token" validate:"required" json:"token" encrypt:"yes"`
}
+type AppKey struct {
+ AppId string `mapstructure:"app_id" validate:"required" json:"app_id"`
+ SecretKey string `mapstructure:"secret_key" validate:"required" json:"secret_key" encrypt:"yes"`
+}
+
type RestConnection struct {
BaseConnection `mapstructure:",squash"`
Endpoint string `mapstructure:"endpoint" validate:"required" json:"endpoint"`
diff --git a/services/init.go b/services/init.go
index dd70c17b..63a69292 100644
--- a/services/init.go
+++ b/services/init.go
@@ -39,7 +39,7 @@ var db *gorm.DB
var cronManager *cron.Cron
var log core.Logger
-func init() {
+func Init() {
var err error
cfg = config.GetConfig()
log = logger.Global
diff --git a/test/api/task/task_test.go b/test/api/task/task_test.go
index 54d88471..1cc712d4 100644
--- a/test/api/task/task_test.go
+++ b/test/api/task/task_test.go
@@ -25,24 +25,33 @@ import (
"testing"
"github.com/apache/incubator-devlake/api"
+ "github.com/apache/incubator-devlake/config"
"github.com/apache/incubator-devlake/models"
+ "github.com/apache/incubator-devlake/plugins/core"
+ "github.com/apache/incubator-devlake/services"
"github.com/gin-gonic/gin"
"github.com/magiconair/properties/assert"
- "github.com/stretchr/testify/mock"
)
+func init() {
+ v := config.GetConfig()
+ encKey := v.GetString(core.EncodeKeyEnvStr)
+ if encKey == "" {
+ // Randomly generate a bunch of encryption keys and set them to config
+ encKey = core.RandomEncKey()
+ v.Set(core.EncodeKeyEnvStr, encKey)
+ err := config.WriteConfig(v)
+ if err != nil {
+ panic(err)
+ }
+ }
+ services.Init()
+}
+
func TestNewTask(t *testing.T) {
r := gin.Default()
api.RegisterRouter(r)
- type services struct {
- mock.Mock
- }
-
- // fakeTask := models.Task{}
- testObj := new(services)
- testObj.On("CreateTask").Return(true, nil)
-
w := httptest.NewRecorder()
params := strings.NewReader(`{"name": "hello", "tasks": [[{ "plugin": "jira", "options": { "host": "www.jira.com" } }]]}`)
req, _ := http.NewRequest("POST", "/pipelines", params)