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 735c181d Issues/2087 create template and icla collect (#2088)
735c181d is described below

commit 735c181d62299962afae82bfb1b9bd5916dc6e0f
Author: likyh <[email protected]>
AuthorDate: Wed Jun 8 16:15:18 2022 +0800

    Issues/2087 create template and icla collect (#2088)
    
    * add some template
    
    * collect apache committer info
    
    * fix a camel
    
    * append
    
    Co-authored-by: linyh <[email protected]>
---
 generator/template/plugin/api_client.go-template   |  71 ++++++++++++++
 .../template/plugin/api_collector.go-template      |  77 +++++++++++++++
 generator/template/plugin/extractor.go-template    |  57 +++++++++++
 generator/template/plugin/plugin_main.go-template  | 105 +++++++++++++++++++++
 generator/template/plugin/task_data.go-template    |  38 ++++++++
 plugins/icla/models/committer.go                   |  32 +++++++
 plugins/icla/plugin_main.go                        | 103 ++++++++++++++++++++
 plugins/icla/tasks/api_client.go                   |  69 ++++++++++++++
 plugins/icla/tasks/committer_collector.go          |  73 ++++++++++++++
 plugins/icla/tasks/committer_extractor.go          |  64 +++++++++++++
 plugins/icla/tasks/task_data.go                    |  36 +++++++
 11 files changed, 725 insertions(+)

diff --git a/generator/template/plugin/api_client.go-template 
b/generator/template/plugin/api_client.go-template
new file mode 100644
index 00000000..8da60666
--- /dev/null
+++ b/generator/template/plugin/api_client.go-template
@@ -0,0 +1,71 @@
+/*
+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 plugin
+
+import (
+       "fmt"
+       "net/http"
+
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
+       "github.com/apache/incubator-devlake/utils"
+)
+
+// TODO add what host would want to requist
+const ENDPOINT = "https://open.example.cn/api/v1";
+
+func New{{ .PluginName }}ApiClient(taskCtx core.TaskContext) 
(*helper.ApiAsyncClient, error) {
+       // load and process configuration
+       token := taskCtx.GetConfig("{{ .PLUGIN_NAME }}_TOKEN")
+       if token == "" {
+               println("invalid {{ .PLUGIN_NAME }}_TOKEN, but ignore this 
error now")
+       }
+       userRateLimit, err := utils.StrToIntOr(taskCtx.GetConfig("{{ 
.PLUGIN_NAME }}_API_REQUESTS_PER_HOUR"), 18000)
+       if err != nil {
+               return nil, err
+       }
+       proxy := taskCtx.GetConfig("{{ .PLUGIN_NAME }}_PROXY")
+
+       // real request apiClient
+       apiClient, err := helper.NewApiClient(ENDPOINT, nil, 0, proxy, 
taskCtx.GetContext())
+       if err != nil {
+               return nil, err
+       }
+       // set token
+       apiClient.SetHeaders(map[string]string{
+               "Authorization": fmt.Sprintf("Bearer %v", token),
+       })
+
+       // TODO add some check after request if necessary
+       // apiClient.SetAfterFunction(func(res *http.Response) error {
+       //    if res.StatusCode == http.StatusUnauthorized {
+       //        return fmt.Errorf("authentication failed, please check your 
Bearer Auth Token")
+       //    }
+       //    return nil
+       // })
+
+       // create async api client
+    asyncApiClient, err := helper.CreateAsyncApiClient(taskCtx, apiClient, 
&helper.ApiRateLimitCalculator{
+               UserRateLimitPerHour: userRateLimit,
+       })
+       if err != nil {
+               return nil, err
+       }
+
+       return asyncApiClient, nil
+}
diff --git a/generator/template/plugin/api_collector.go-template 
b/generator/template/plugin/api_collector.go-template
new file mode 100644
index 00000000..c744801d
--- /dev/null
+++ b/generator/template/plugin/api_collector.go-template
@@ -0,0 +1,77 @@
+/*
+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 (
+       "encoding/json"
+       "net/http"
+       "net/url"
+       "strconv"
+
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+const RAW_{{ .COLLECTOR_DATA_NAME }}_TABLE = "{{ .pluginName }}_{{ 
.collector_data_name }}"
+
+var _ core.SubTaskEntryPoint = Collect{{ .CollectorDataName }}
+
+func Collect{{ .CollectorDataName }}(taskCtx core.SubTaskContext) error {
+       data := taskCtx.GetData().(*{{ .PluginName }}TaskData)
+       iterator, err := helper.NewDateIterator(365)
+       if err != nil {
+               return err
+       }
+
+       collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+               RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+                       Ctx: taskCtx,
+                       Params: {{ .PluginName }}ApiParams{
+                       },
+                       Table: RAW_{{ .COLLECTOR_DATA_NAME }}_TABLE,
+               },
+               ApiClient:   data.ApiClient,
+               Incremental: false,
+               Input:       iterator,
+               // TODO write which api would you want request
+               UrlTemplate: "/example/",
+               Query: func(reqData *helper.RequestData) (url.Values, error) {
+                       query := url.Values{}
+                       input := reqData.Input.(*helper.DatePair)
+                       query.Set("start_time", 
strconv.FormatInt(input.PairStartTime.Unix(), 10))
+                       query.Set("end_time", 
strconv.FormatInt(input.PairEndTime.Unix(), 10))
+                       return query, nil
+               },
+               ResponseParser: func(res *http.Response) ([]json.RawMessage, 
error) {
+                       // TODO decode result from api request
+                       return []json.RawMessage{}, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return collector.Execute()
+}
+
+var Collect{{ .CollectorDataName }}Meta = core.SubTaskMeta{
+       Name:             "Collect{{ .CollectorDataName }}",
+       EntryPoint:       Collect{{ .CollectorDataName }},
+       EnabledByDefault: true,
+       Description:      "Collect {{ .CollectorDataName }} data from {{ 
.PluginName }} api",
+}
diff --git a/generator/template/plugin/extractor.go-template 
b/generator/template/plugin/extractor.go-template
new file mode 100644
index 00000000..eceb661c
--- /dev/null
+++ b/generator/template/plugin/extractor.go-template
@@ -0,0 +1,57 @@
+/*
+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 (
+       "encoding/json"
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+var _ core.SubTaskEntryPoint = Extract{{ .ExtractorDataName }}
+
+func Extract{{ .ExtractorDataName }}(taskCtx core.SubTaskContext) error {
+    extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+               RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+                       Ctx: taskCtx,
+                       Params: {{ .PluginName }}ApiParams{
+                       },
+                       Table: RAW_{{ .COLLECTOR_DATA_NAME }}_TABLE,
+               },
+               Extract: func(resData *helper.RawData) ([]interface{}, error) {
+                       extractedModels := make([]interface{}, 0)
+                       println(resData.Data)
+                       println(resData.Input)
+                       // TODO decode some db models from api result
+                       // extractedModels = append(extractedModels, 
&models.XXXXXX)
+                       return extractedModels, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return extractor.Execute()
+}
+
+var Extract{{ .ExtractorDataName }}Meta = core.SubTaskMeta{
+       Name:             "Extract{{ .ExtractorDataName }}",
+       EntryPoint:       Extract{{ .ExtractorDataName }},
+       EnabledByDefault: true,
+       Description:      "Extract raw data into tool layer table {{ 
.plugin_name }}_{{ .extractor_data_name }}",
+}
diff --git a/generator/template/plugin/plugin_main.go-template 
b/generator/template/plugin/plugin_main.go-template
new file mode 100644
index 00000000..ba5b3feb
--- /dev/null
+++ b/generator/template/plugin/plugin_main.go-template
@@ -0,0 +1,105 @@
+/*
+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 main
+
+import (
+       "context"
+       "github.com/apache/incubator-devlake/migration"
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/{{ .pluginName }}/tasks"
+       "github.com/apache/incubator-devlake/runner"
+       "github.com/mitchellh/mapstructure"
+       "github.com/spf13/cobra"
+       "github.com/spf13/viper"
+       "gorm.io/gorm"
+       "time"
+)
+
+// make sure interface is implemented
+var _ core.PluginMeta = (*{{ .PluginName }})(nil)
+var _ core.PluginInit = (*{{ .PluginName }})(nil)
+var _ core.PluginTask = (*{{ .PluginName }})(nil)
+var _ core.PluginApi = (*{{ .PluginName }})(nil)
+
+// Export a variable named PluginEntry for Framework to search and load
+var PluginEntry {{ .PluginName }} //nolint
+
+type {{ .PluginName }} struct{}
+
+func (plugin {{ .PluginName }}) Description() string {
+       return "collect some {{ .PluginName }} data"
+}
+
+func (plugin {{ .PluginName }}) Init(config *viper.Viper, logger core.Logger, 
db *gorm.DB) error {
+       // AutoSchemas is a **develop** script to auto migrate models easily.
+       // FIXME Don't submit it as a open source plugin
+       return db.Migrator().AutoMigrate(
+               // TODO add your models in here
+       )
+}
+
+func (plugin {{ .PluginName }}) SubTaskMetas() []core.SubTaskMeta {
+       return []core.SubTaskMeta{
+               // TODO add your sub task here
+       }
+}
+
+func (plugin {{ .PluginName }}) PrepareTaskData(taskCtx core.TaskContext, 
options map[string]interface{}) (interface{}, error) {
+       var op tasks.{{ .PluginName }}Options
+       err := mapstructure.Decode(options, &op)
+       if err != nil {
+               return nil, err
+       }
+
+       // apiClient, err := tasks.New{{ .PluginName }}ApiClient(taskCtx)
+       // if err != nil {
+       //      return nil, err
+       // }
+
+       return &tasks.{{ .PluginName }}TaskData{
+               Options: &op,
+               // TODO you can init and stash some handler to deal data at all 
subtasks, Such as apiClient as below.
+               // NOTES: In task_data.go/TaskData should declare `ApiClient`
+        // ApiClient: apiClient,
+       }, nil
+}
+
+// PkgPath information lost when compiled as plugin(.so)
+func (plugin {{ .PluginName }}) RootPkgPath() string {
+       return "github.com/apache/incubator-devlake/plugins/{{ .pluginName }}"
+}
+
+func (plugin {{ .PluginName }}) ApiResources() 
map[string]map[string]core.ApiResourceHandler {
+       return nil
+}
+
+// standalone mode for debugging
+func main() {
+       cmd := &cobra.Command{Use: "{{ .pluginName }}"}
+
+       // TODO add your cmd flag if necessary
+       // yourFlag := cmd.Flags().IntP("yourFlag", "y", 8, "TODO add 
description here")
+       // _ = cmd.MarkFlagRequired("yourFlag")
+
+       cmd.Run = func(cmd *cobra.Command, args []string) {
+               runner.DirectRun(cmd, args, PluginEntry, []string{}, 
map[string]interface{}{
+                       // TODO add more custom params here
+               })
+       }
+       runner.RunCmd(cmd)
+}
diff --git a/generator/template/plugin/task_data.go-template 
b/generator/template/plugin/task_data.go-template
new file mode 100644
index 00000000..4498596e
--- /dev/null
+++ b/generator/template/plugin/task_data.go-template
@@ -0,0 +1,38 @@
+/*
+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 plugin
+
+import (
+       "github.com/apache/incubator-devlake/plugins/helper"
+)
+
+type {{ .PluginName }}ApiParams struct {
+}
+
+type {{ .PluginName }}Options struct {
+       // TODO add some custom options here if necessary
+       // options means some custom params required by plugin running.
+       // Such As How many rows do your want
+       // You can use it in sub tasks and you need pass it in main.go and 
pipelines.
+       Tasks []string `json:"tasks,omitempty"`
+}
+
+type {{ .PluginName }}TaskData struct {
+       Options   *{{ .PluginName }}Options
+       // ApiClient *helper.ApiAsyncClient
+}
diff --git a/plugins/icla/models/committer.go b/plugins/icla/models/committer.go
new file mode 100644
index 00000000..b75b50aa
--- /dev/null
+++ b/plugins/icla/models/committer.go
@@ -0,0 +1,32 @@
+/*
+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 models
+
+import (
+       "github.com/apache/incubator-devlake/models/common"
+)
+
+type IclaCommitter struct {
+       UserName string `gorm:"primaryKey;type:varchar(255)"`
+       Name     string `gorm:"primaryKey;type:varchar(255)"`
+       common.NoPKModel
+}
+
+func (IclaCommitter) TableName() string {
+       return "_tool_icla_committer"
+}
diff --git a/plugins/icla/plugin_main.go b/plugins/icla/plugin_main.go
new file mode 100644
index 00000000..5bcf2345
--- /dev/null
+++ b/plugins/icla/plugin_main.go
@@ -0,0 +1,103 @@
+/*
+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 main
+
+import (
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/icla/models"
+       "github.com/apache/incubator-devlake/plugins/icla/tasks"
+       "github.com/apache/incubator-devlake/runner"
+       "github.com/mitchellh/mapstructure"
+       "github.com/spf13/cobra"
+       "github.com/spf13/viper"
+       "gorm.io/gorm"
+)
+
+// make sure interface is implemented
+var _ core.PluginMeta = (*Icla)(nil)
+var _ core.PluginInit = (*Icla)(nil)
+var _ core.PluginTask = (*Icla)(nil)
+var _ core.PluginApi = (*Icla)(nil)
+
+// Export a variable named PluginEntry for Framework to search and load
+var PluginEntry Icla //nolint
+
+type Icla struct{}
+
+func (plugin Icla) Description() string {
+       return "collect some Icla data"
+}
+
+func (plugin Icla) Init(config *viper.Viper, logger core.Logger, db *gorm.DB) 
error {
+       // AutoSchemas is a **develop** script to auto migrate models easily.
+       // FIXME Don't submit it as a open source plugin
+       return db.Migrator().AutoMigrate(
+               // TODO add your models in here
+               &models.IclaCommitter{},
+       )
+}
+
+func (plugin Icla) SubTaskMetas() []core.SubTaskMeta {
+       return []core.SubTaskMeta{
+               tasks.CollectCommitterMeta,
+               tasks.ExtractCommitterMeta,
+       }
+}
+
+func (plugin Icla) PrepareTaskData(taskCtx core.TaskContext, options 
map[string]interface{}) (interface{}, error) {
+       var op tasks.IclaOptions
+       err := mapstructure.Decode(options, &op)
+       if err != nil {
+               return nil, err
+       }
+
+       apiClient, err := tasks.NewIclaApiClient(taskCtx)
+       if err != nil {
+               return nil, err
+       }
+
+       return &tasks.IclaTaskData{
+               Options:   &op,
+               ApiClient: apiClient,
+       }, nil
+}
+
+// PkgPath information lost when compiled as plugin(.so)
+func (plugin Icla) RootPkgPath() string {
+       return "github.com/apache/incubator-devlake/plugins/icla"
+}
+
+func (plugin Icla) ApiResources() 
map[string]map[string]core.ApiResourceHandler {
+       return nil
+}
+
+// standalone mode for debugging
+func main() {
+       cmd := &cobra.Command{Use: "icla"}
+
+       // TODO add your cmd flag if necessary
+       // yourFlag := cmd.Flags().IntP("yourFlag", "y", 8, "TODO add 
description here")
+       // _ = cmd.MarkFlagRequired("yourFlag")
+
+       cmd.Run = func(cmd *cobra.Command, args []string) {
+               runner.DirectRun(cmd, args, PluginEntry, []string{}, 
map[string]interface{}{
+                       // TODO add more custom params here
+               })
+       }
+       runner.RunCmd(cmd)
+}
diff --git a/plugins/icla/tasks/api_client.go b/plugins/icla/tasks/api_client.go
new file mode 100644
index 00000000..6e4944ab
--- /dev/null
+++ b/plugins/icla/tasks/api_client.go
@@ -0,0 +1,69 @@
+/*
+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/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
+       "github.com/apache/incubator-devlake/utils"
+       "net/http"
+)
+
+const ENDPOINT = "https://people.apache.org/";
+
+func NewIclaApiClient(taskCtx core.TaskContext) (*helper.ApiAsyncClient, 
error) {
+       // load and process configuration
+       token := taskCtx.GetConfig("ICLA_TOKEN")
+       if token == "" {
+               println("invalid ICLA_TOKEN, but ignore this error now")
+       }
+       userRateLimit, err := 
utils.StrToIntOr(taskCtx.GetConfig("ICLA_API_REQUESTS_PER_HOUR"), 18000)
+       if err != nil {
+               return nil, err
+       }
+       proxy := taskCtx.GetConfig("ICLA_PROXY")
+
+       // real request apiClient
+       apiClient, err := helper.NewApiClient(ENDPOINT, nil, 0, proxy, 
taskCtx.GetContext())
+       if err != nil {
+               return nil, err
+       }
+       // set token
+       apiClient.SetHeaders(map[string]string{
+               "Authorization": fmt.Sprintf("Bearer %v", token),
+       })
+
+       // TODO add some check after request if necessary
+       apiClient.SetAfterFunction(func(res *http.Response) error {
+               if res.StatusCode == http.StatusUnauthorized {
+                       return fmt.Errorf("authentication failed, please check 
your Bearer Auth Token")
+               }
+               return nil
+       })
+
+       // create async api client
+       asyncApiClient, err := helper.CreateAsyncApiClient(taskCtx, apiClient, 
&helper.ApiRateLimitCalculator{
+               UserRateLimitPerHour: userRateLimit,
+       })
+       if err != nil {
+               return nil, err
+       }
+
+       return asyncApiClient, nil
+}
diff --git a/plugins/icla/tasks/committer_collector.go 
b/plugins/icla/tasks/committer_collector.go
new file mode 100644
index 00000000..484b2f1c
--- /dev/null
+++ b/plugins/icla/tasks/committer_collector.go
@@ -0,0 +1,73 @@
+/*
+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 (
+       "encoding/json"
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
+       "net/http"
+       "net/url"
+)
+
+const RAW_COMMITTER_TABLE = "icla_committer"
+
+var _ core.SubTaskEntryPoint = CollectCommitter
+
+func CollectCommitter(taskCtx core.SubTaskContext) error {
+       data := taskCtx.GetData().(*IclaTaskData)
+
+       collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{
+               RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+                       Ctx:    taskCtx,
+                       Params: IclaApiParams{},
+                       Table:  RAW_COMMITTER_TABLE,
+               },
+               ApiClient:   data.ApiClient,
+               Incremental: false,
+               UrlTemplate: "public/icla-info.json",
+               Query: func(reqData *helper.RequestData) (url.Values, error) {
+                       query := url.Values{}
+                       return query, nil
+               },
+               ResponseParser: func(res *http.Response) ([]json.RawMessage, 
error) {
+                       body := &struct {
+                               LastUpdated string          
`json:"last_updated"`
+                               Committers  json.RawMessage `json:"committers"`
+                       }{}
+                       err := helper.UnmarshalResponse(res, body)
+                       if err != nil {
+                               return nil, err
+                       }
+                       println("receive data:", len(body.Committers))
+                       return []json.RawMessage{body.Committers}, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return collector.Execute()
+}
+
+var CollectCommitterMeta = core.SubTaskMeta{
+       Name:             "CollectCommitter",
+       EntryPoint:       CollectCommitter,
+       EnabledByDefault: true,
+       Description:      "Collect Committer data from Icla api",
+}
diff --git a/plugins/icla/tasks/committer_extractor.go 
b/plugins/icla/tasks/committer_extractor.go
new file mode 100644
index 00000000..74ca28ca
--- /dev/null
+++ b/plugins/icla/tasks/committer_extractor.go
@@ -0,0 +1,64 @@
+/*
+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 (
+       "encoding/json"
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/helper"
+       "github.com/apache/incubator-devlake/plugins/icla/models"
+)
+
+var _ core.SubTaskEntryPoint = ExtractCommitter
+
+func ExtractCommitter(taskCtx core.SubTaskContext) error {
+       extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
+               RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+                       Ctx:    taskCtx,
+                       Params: IclaApiParams{},
+                       Table:  RAW_COMMITTER_TABLE,
+               },
+               Extract: func(resData *helper.RawData) ([]interface{}, error) {
+                       names := &map[string]string{}
+                       err := json.Unmarshal(resData.Data, names)
+                       if err != nil {
+                               return nil, err
+                       }
+                       extractedModels := make([]interface{}, 0)
+                       for userName, name := range *names {
+                               extractedModels = append(extractedModels, 
&models.IclaCommitter{
+                                       UserName: userName,
+                                       Name:     name,
+                               })
+                       }
+                       return extractedModels, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return extractor.Execute()
+}
+
+var ExtractCommitterMeta = core.SubTaskMeta{
+       Name:             "ExtractCommitter",
+       EntryPoint:       ExtractCommitter,
+       EnabledByDefault: true,
+       Description:      "Extract raw data into tool layer table {{ 
.plugin_name }}_{{ .extractor_data_name }}",
+}
diff --git a/plugins/icla/tasks/task_data.go b/plugins/icla/tasks/task_data.go
new file mode 100644
index 00000000..f6b2de5f
--- /dev/null
+++ b/plugins/icla/tasks/task_data.go
@@ -0,0 +1,36 @@
+/*
+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 "github.com/apache/incubator-devlake/plugins/helper"
+
+type IclaApiParams struct {
+}
+
+type IclaOptions struct {
+       // TODO add some custom options here if necessary
+       // options means some custom params required by plugin running.
+       // Such As How many rows do your want
+       // You can use it in sub tasks and you need pass it in main.go and 
pipelines.
+       Tasks []string `json:"tasks,omitempty"`
+}
+
+type IclaTaskData struct {
+       Options   *IclaOptions
+       ApiClient *helper.ApiAsyncClient
+}

Reply via email to