Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion backend/plugins/taiga/models/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ limitations under the License.
package models

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"

"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/utils"
Expand All @@ -39,14 +44,65 @@ func (tc *TaigaConn) Sanitize() TaigaConn {
return *tc
}

// SetupAuthentication sets up the HTTP request with authentication
// SetupAuthentication sets up the HTTP request with authentication.
// If Token is set directly, use it. Otherwise exchange Username+Password for a token.
func (tc *TaigaConn) SetupAuthentication(req *http.Request) errors.Error {
if tc.Token != "" {
req.Header.Set("Authorization", "Bearer "+tc.Token)
return nil
}
if tc.Username != "" && tc.Password != "" {
token, err := tc.fetchToken()
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
}
return nil
}

// fetchToken exchanges username+password for a Taiga auth token via POST /auth
func (tc *TaigaConn) fetchToken() (string, errors.Error) {
endpoint := strings.TrimSuffix(tc.Endpoint, "/")
// strip /api/v1 suffix to get base, then re-add /api/v1/auth
authURL := endpoint + "/auth"

body, e := json.Marshal(map[string]string{
"type": "normal",
"username": tc.Username,
"password": tc.Password,
})
if e != nil {
return "", errors.Default.WrapRaw(e)
}

resp, e := http.Post(authURL, "application/json", bytes.NewReader(body)) //nolint:noctx
if e != nil {
return "", errors.Default.WrapRaw(e)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", errors.Default.New(fmt.Sprintf("taiga auth failed with status %d", resp.StatusCode))
}

var result map[string]interface{}
if e := json.NewDecoder(resp.Body).Decode(&result); e != nil {
return "", errors.Default.WrapRaw(e)
}
// Taiga returns auth_token (v5) or token (v6)
for _, key := range []string{"auth_token", "token"} {
if t, ok := result[key]; ok {
if token, ok := t.(string); ok && token != "" {
return token, nil
}
}
}
// fallback: read raw body hint
raw, _ := io.ReadAll(bytes.NewReader(body))
return "", errors.Default.New(fmt.Sprintf("taiga auth response missing token field, body: %s", string(raw)))
}

// TaigaConnection holds TaigaConn plus ID/Name for database storage
type TaigaConnection struct {
helper.BaseConnection `mapstructure:",squash"`
Expand Down
13 changes: 9 additions & 4 deletions config-ui/src/plugins/register/taiga/connection-fields/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
*/

import { useState, useEffect } from 'react';
import { useEffect } from 'react';
import { Input } from 'antd';

interface Props {
Expand Down Expand Up @@ -72,7 +72,7 @@ export const Auth = ({ type, initialValues, values, setValues, setErrors }: Prop
</label>
<Input
style={{ width: 386 }}
placeholder="https://api.taiga.io/"
placeholder="https://projects.example.com/api/v1/"
value={values.endpoint}
onChange={handleChangeEndpoint}
/>
Expand All @@ -85,7 +85,12 @@ export const Auth = ({ type, initialValues, values, setValues, setErrors }: Prop
<span className="label">Username</span>
<span className="required">*</span>
</label>
<Input style={{ width: 386 }} placeholder="username" value={values.username} onChange={handleChangeUsername} />
<Input
style={{ width: 386 }}
placeholder="username or email"
value={values.username}
onChange={handleChangeUsername}
/>
</div>
<div className="form-item">
<label>
Expand All @@ -94,7 +99,7 @@ export const Auth = ({ type, initialValues, values, setValues, setErrors }: Prop
</label>
<Input.Password
style={{ width: 386 }}
placeholder="password"
placeholder={type === 'update' ? '********' : 'password'}
value={values.password}
onChange={handleChangePassword}
/>
Expand Down
90 changes: 67 additions & 23 deletions grafana/dashboards/DeveloperTelemetry.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"editorMode": "code",
"format": "time_series",
"rawQuery": true,
"rawSql": "SELECT\n date as time,\n developer_id as metric,\n active_hours as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nORDER BY date",
"rawSql": "SELECT\n date as time,\n developer_id as metric,\n active_hours as value\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nORDER BY date",
"refId": "A",
"sql": {
"columns": [
Expand Down Expand Up @@ -209,7 +209,7 @@
"editorMode": "code",
"format": "time_series",
"rawQuery": true,
"rawSql": "SELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\n AND JSON_EXTRACT(git_activity, '$.total_commits') IS NOT NULL\nORDER BY time_sec",
"rawSql": "SELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\n AND JSON_EXTRACT(git_activity, '$.total_commits') IS NOT NULL\nORDER BY time_sec",
"refId": "A"
}
],
Expand Down Expand Up @@ -285,7 +285,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n developer_id,\n CAST(SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) AS UNSIGNED) as lines_added\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY developer_id\nORDER BY lines_added DESC",
"rawSql": "SELECT\n developer_id,\n CAST(SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) AS UNSIGNED) as lines_added\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nGROUP BY developer_id\nORDER BY lines_added DESC",
"refId": "A"
}
],
Expand Down Expand Up @@ -381,7 +381,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n developer_id,\n CAST(SUM(active_hours) AS UNSIGNED) as hours\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY developer_id\nORDER BY hours DESC\nLIMIT 15",
"rawSql": "SELECT\n developer_id,\n CAST(SUM(active_hours) AS UNSIGNED) as hours\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nGROUP BY developer_id\nORDER BY hours DESC\nLIMIT 15",
"refId": "A"
}
],
Expand Down Expand Up @@ -468,7 +468,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n DAYNAME(date) as day_name,\n AVG(active_hours) as avg_hours\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY DAYOFWEEK(date), DAYNAME(date)\nORDER BY DAYOFWEEK(date)",
"rawSql": "SELECT\n DAYNAME(date) as day_name,\n AVG(active_hours) as avg_hours\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nGROUP BY DAYOFWEEK(date), DAYNAME(date)\nORDER BY DAYOFWEEK(date)",
"refId": "A"
}
],
Expand Down Expand Up @@ -507,7 +507,10 @@
"id": 6,
"options": {
"legend": {
"calcs": ["mean", "max"],
"calcs": [
"mean",
"max"
],
"displayMode": "table",
"placement": "right",
"showLegend": true
Expand All @@ -527,7 +530,7 @@
"editorMode": "code",
"format": "time_series",
"rawQuery": true,
"rawSql": "WITH top_devs AS (\n SELECT developer_id\n FROM _tool_developer_metrics\n WHERE connection_id = 2 AND $__timeFilter(date)\n GROUP BY developer_id\n ORDER BY SUM(CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED)) DESC\n LIMIT 10\n)\nSELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\n AND developer_id IN (SELECT developer_id FROM top_devs)\nORDER BY time_sec",
"rawSql": "WITH top_devs AS (\n SELECT developer_id\n FROM _tool_developer_metrics\n WHERE connection_id = ${connection_id} AND $__timeFilter(date)\n GROUP BY developer_id\n ORDER BY SUM(CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED)) DESC\n LIMIT 10\n)\nSELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(git_activity, '$.total_commits') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\n AND developer_id IN (SELECT developer_id FROM top_devs)\nORDER BY time_sec",
"refId": "A"
}
],
Expand Down Expand Up @@ -650,7 +653,7 @@
"editorMode": "code",
"format": "time_series",
"rawQuery": true,
"rawSql": "SELECT\n date as time,\n 'lines_added' as metric,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY date\nUNION ALL\nSELECT\n date as time,\n 'lines_deleted' as metric,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_deleted')) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY date\nORDER BY time",
"rawSql": "SELECT\n date as time,\n 'lines_added' as metric,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as value\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nGROUP BY date\nUNION ALL\nSELECT\n date as time,\n 'lines_deleted' as metric,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_deleted')) as value\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nGROUP BY date\nORDER BY time",
"refId": "A"
}
],
Expand Down Expand Up @@ -719,7 +722,9 @@
"id": 8,
"options": {
"legend": {
"calcs": ["mean"],
"calcs": [
"mean"
],
"displayMode": "table",
"placement": "right",
"showLegend": true
Expand All @@ -738,7 +743,7 @@
"editorMode": "code",
"format": "time_series",
"rawQuery": true,
"rawSql": "SELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(development_activity, '$.test_runs_detected') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\n AND JSON_EXTRACT(development_activity, '$.test_runs_detected') > 0\nORDER BY time_sec",
"rawSql": "SELECT\n UNIX_TIMESTAMP(date) as time_sec,\n developer_id as metric,\n CAST(JSON_EXTRACT(development_activity, '$.test_runs_detected') AS UNSIGNED) as value\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\n AND JSON_EXTRACT(development_activity, '$.test_runs_detected') > 0\nORDER BY time_sec",
"refId": "A"
}
],
Expand Down Expand Up @@ -827,7 +832,9 @@
"footer": {
"countRows": false,
"fields": "",
"reducer": ["sum"],
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
Expand All @@ -848,7 +855,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n developer_id,\n name,\n email,\n SUM(JSON_EXTRACT(git_activity, '$.total_commits')) as total_commits,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as total_lines_added,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_deleted')) as total_lines_deleted,\n AVG(active_hours) as avg_active_hours,\n SUM(active_hours) as total_active_hours\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)\nGROUP BY developer_id, name, email\nORDER BY total_commits DESC",
"rawSql": "SELECT\n developer_id,\n name,\n email,\n SUM(JSON_EXTRACT(git_activity, '$.total_commits')) as total_commits,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as total_lines_added,\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_deleted')) as total_lines_deleted,\n AVG(active_hours) as avg_active_hours,\n SUM(active_hours) as total_active_hours\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)\nGROUP BY developer_id, name, email\nORDER BY total_commits DESC",
"refId": "A"
}
],
Expand Down Expand Up @@ -893,7 +900,9 @@
"orientation": "auto",
"reduceOptions": {
"values": false,
"calcs": ["sum"],
"calcs": [
"sum"
],
"fields": ""
},
"textMode": "auto"
Expand All @@ -908,7 +917,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n SUM(JSON_EXTRACT(git_activity, '$.total_commits')) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
"rawSql": "SELECT\n SUM(JSON_EXTRACT(git_activity, '$.total_commits')) as total\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)",
"refId": "A"
}
],
Expand Down Expand Up @@ -953,7 +962,9 @@
"orientation": "auto",
"reduceOptions": {
"values": false,
"calcs": ["sum"],
"calcs": [
"sum"
],
"fields": ""
},
"textMode": "auto"
Expand All @@ -968,7 +979,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n SUM(active_hours) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
"rawSql": "SELECT\n SUM(active_hours) as total\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)",
"refId": "A"
}
],
Expand Down Expand Up @@ -1013,7 +1024,9 @@
"orientation": "auto",
"reduceOptions": {
"values": false,
"calcs": ["sum"],
"calcs": [
"sum"
],
"fields": ""
},
"textMode": "auto"
Expand All @@ -1028,7 +1041,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
"rawSql": "SELECT\n SUM(JSON_EXTRACT(git_activity, '$.total_lines_added')) as total\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)",
"refId": "A"
}
],
Expand Down Expand Up @@ -1073,7 +1086,9 @@
"orientation": "auto",
"reduceOptions": {
"values": false,
"calcs": ["lastNotNull"],
"calcs": [
"lastNotNull"
],
"fields": ""
},
"textMode": "auto"
Expand All @@ -1088,7 +1103,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "SELECT\n COUNT(DISTINCT developer_id) as total\nFROM _tool_developer_metrics\nWHERE connection_id = 2\n AND $__timeFilter(date)",
"rawSql": "SELECT\n COUNT(DISTINCT developer_id) as total\nFROM _tool_developer_metrics\nWHERE connection_id = ${connection_id}\n AND $__timeFilter(date)",
"refId": "A"
}
],
Expand All @@ -1099,9 +1114,38 @@
"refresh": "",
"schemaVersion": 38,
"style": "dark",
"tags": ["developer", "telemetry", "productivity"],
"tags": [
"developer",
"telemetry",
"productivity"
],
"templating": {
"list": []
"list": [
{
"current": {
"selected": true,
"text": "4",
"value": "4"
},
"datasource": {
"type": "mysql",
"uid": "mysql"
},
"definition": "SELECT DISTINCT connection_id FROM _tool_developer_metrics ORDER BY connection_id",
"hide": 0,
"includeAll": false,
"label": "Connection ID",
"multi": false,
"name": "connection_id",
"options": [],
"query": "SELECT DISTINCT connection_id FROM _tool_developer_metrics ORDER BY connection_id",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"time": {
"from": "now-6w",
Expand All @@ -1113,4 +1157,4 @@
"uid": "developer-telemetry",
"version": 0,
"weekStart": ""
}
}