From d17bc64a5a2bf623f74c6f0576b71cc29d102cf4 Mon Sep 17 00:00:00 2001 From: warren Date: Sat, 29 Aug 2026 08:59:05 +0800 Subject: [PATCH 1/2] fix(kiro): report successful connection tests Signed-off-by: warren --- backend/plugins/kiro/api/test_connection.go | 23 ++++++-- .../plugins/kiro/api/test_connection_test.go | 56 +++++++++++++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 backend/plugins/kiro/api/test_connection_test.go diff --git a/backend/plugins/kiro/api/test_connection.go b/backend/plugins/kiro/api/test_connection.go index 8df055e582f..de9fea37360 100644 --- a/backend/plugins/kiro/api/test_connection.go +++ b/backend/plugins/kiro/api/test_connection.go @@ -95,6 +95,11 @@ func TestExistingConnection(input *plugin.ApiResourceInput) (*plugin.ApiResource // discovered accounts and per-stream object counts makes the difference visible // before any scope is created. type ConnectionReport struct { + // Success and Message satisfy the shared Config UI connection-test contract. + // The detailed fields below remain available to explain an empty export. + Success bool `json:"success"` + Message string `json:"message"` + ReportBucket string `json:"reportBucket"` PromptLogBucket string `json:"promptLogBucket"` // Accounts are the AWS account ids found under the report prefix. An empty @@ -111,18 +116,24 @@ type ConnectionReport struct { // verifying a path - only whether it is zero. const connectionReportCountLimit = 500 +func newConnectionReport(connection *models.KiroConnection) *ConnectionReport { + return &ConnectionReport{ + Success: true, + Message: "success", + ReportBucket: connection.Bucket, + PromptLogBucket: connection.GetPromptLogBucket(), + Accounts: []string{}, + Streams: []tasks.StreamCount{}, + } +} + // buildConnectionReport probes the layout and summarizes what was found. // // Errors are folded into the report rather than returned: the connection itself // is already known to work at this point, and a discovery failure is more useful // shown as an empty result with a hint than as a failed request. func buildConnectionReport(connection *models.KiroConnection) *ConnectionReport { - report := &ConnectionReport{ - ReportBucket: connection.Bucket, - PromptLogBucket: connection.GetPromptLogBucket(), - Accounts: []string{}, - Streams: []tasks.StreamCount{}, - } + report := newConnectionReport(connection) discovery, err := tasks.NewDiscovery(connection) if err != nil { diff --git a/backend/plugins/kiro/api/test_connection_test.go b/backend/plugins/kiro/api/test_connection_test.go new file mode 100644 index 00000000000..7b30ca366f9 --- /dev/null +++ b/backend/plugins/kiro/api/test_connection_test.go @@ -0,0 +1,56 @@ +/* +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 api + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/apache/incubator-devlake/plugins/kiro/models" +) + +func TestNewConnectionReportSatisfiesConfigUIContract(t *testing.T) { + connection := &models.KiroConnection{ + KiroConn: models.KiroConn{ + Bucket: "report-bucket", + PromptLogBucket: "log-bucket", + }, + } + + report := newConnectionReport(connection) + assert.True(t, report.Success) + assert.Equal(t, "success", report.Message) + assert.Equal(t, "report-bucket", report.ReportBucket) + assert.Equal(t, "log-bucket", report.PromptLogBucket) + assert.NotNil(t, report.Accounts) + assert.NotNil(t, report.Streams) + + body, err := json.Marshal(report) + require.NoError(t, err) + assert.JSONEq(t, `{ + "success": true, + "message": "success", + "reportBucket": "report-bucket", + "promptLogBucket": "log-bucket", + "accounts": [], + "streams": [] + }`, string(body)) +} From 61ec861f33d3ddf9874a6d2c093cd9719ea8cbbf Mon Sep 17 00:00:00 2001 From: warren Date: Sat, 29 Aug 2026 08:59:36 +0800 Subject: [PATCH 2/2] fix(grafana): repair PostgreSQL Kiro queries Signed-off-by: warren --- grafana/dashboards/postgresql/kiro_executive.json | 10 +++++----- grafana/dashboards/postgresql/kiro_logging.json | 14 +++++++------- .../dashboards/postgresql/kiro_user_report.json | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/grafana/dashboards/postgresql/kiro_executive.json b/grafana/dashboards/postgresql/kiro_executive.json index 9a13ba80109..f1a8711be0e 100644 --- a/grafana/dashboards/postgresql/kiro_executive.json +++ b/grafana/dashboards/postgresql/kiro_executive.json @@ -126,7 +126,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT COUNT(DISTINCT user_id) AS \"WAU\" FROM lake._tool_kiro_chat_log WHERE timestamp >= NOW() - INTERVAL '7 DAY'", + "rawSql": "SELECT COUNT(DISTINCT user_id) AS \"WAU\" FROM _tool_kiro_chat_log WHERE timestamp >= NOW() - INTERVAL '7 DAY'", "refId": "A" } ], @@ -322,7 +322,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT ROUND(CAST(CAST(COUNT(DISTINCT CASE WHEN has_steering = TRUE THEN user_id END) AS NUMERIC) / NULLIF(NULLIF(COUNT(DISTINCT user_id), 0), 0) * 100 AS DECIMAL), 0) AS \"Steering %\" FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp)", + "rawSql": "SELECT ROUND(CAST(CAST(COUNT(DISTINCT CASE WHEN has_steering = TRUE THEN user_id END) AS NUMERIC) / NULLIF(NULLIF(COUNT(DISTINCT user_id), 0), 0) * 100 AS DECIMAL), 0) AS \"Steering %\" FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp)", "refId": "A" } ], @@ -433,7 +433,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "SELECT TO_DATE(yw || ' Monday', 'IYYYIW FMDay') AS time, COUNT(DISTINCT user_id) AS \"Active Users\" FROM (SELECT user_id, (EXTRACT(ISOYEAR FROM timestamp) * 100 + EXTRACT(WEEK FROM timestamp))::int AS yw FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp)) AS t GROUP BY \"yw\" ORDER BY time NULLS FIRST", + "rawSql": "SELECT TO_DATE(yw || ' Monday', 'IYYYIW FMDay') AS time, COUNT(DISTINCT user_id) AS \"Active Users\" FROM (SELECT user_id, (EXTRACT(ISOYEAR FROM timestamp) * 100 + EXTRACT(WEEK FROM timestamp))::int AS yw FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp)) AS t GROUP BY \"yw\" ORDER BY time NULLS FIRST", "refId": "A" } ], @@ -642,7 +642,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "SELECT date AS time, SUM(SUM(credits_used)) OVER (ORDER BY date NULLS FIRST) AS \"Cumulative Credits\", (SELECT CAST(SUM(credits_used) AS NUMERIC) / NULLIF(COUNT(DISTINCT date), 0) * EXTRACT(DAY FROM CAST(CAST(DATE_TRUNC('MONTH', CURRENT_DATE) + INTERVAL '1 MONTH' - INTERVAL '1 DAY' AS DATE) AS DATE)) FROM lake._tool_kiro_user_report WHERE YEAR(CAST(date AS DATE)) = YEAR(CAST(CURRENT_DATE AS DATE)) AND MONTH(CAST(date AS DATE)) = MONTH(CAST(CURRENT_DATE AS DATE))) AS \"Projected Monthly\" FROM lake._tool_kiro_user_report WHERE YEAR(CAST(date AS DATE)) = YEAR(CAST(CURRENT_DATE AS DATE)) AND MONTH(CAST(date AS DATE)) = MONTH(CAST(CURRENT_DATE AS DATE)) GROUP BY \"date\" ORDER BY date NULLS FIRST", + "rawSql": "SELECT date AS time, SUM(SUM(credits_used)) OVER (ORDER BY date NULLS FIRST) AS \"Cumulative Credits\", (SELECT CAST(SUM(credits_used) AS NUMERIC) / NULLIF(COUNT(DISTINCT date), 0) * EXTRACT(DAY FROM CAST(CAST(DATE_TRUNC('MONTH', CURRENT_DATE) + INTERVAL '1 MONTH' - INTERVAL '1 DAY' AS DATE) AS DATE)) FROM _tool_kiro_user_report WHERE EXTRACT(YEAR FROM date) = EXTRACT(YEAR FROM CURRENT_DATE) AND EXTRACT(MONTH FROM date) = EXTRACT(MONTH FROM CURRENT_DATE)) AS \"Projected Monthly\" FROM _tool_kiro_user_report WHERE EXTRACT(YEAR FROM date) = EXTRACT(YEAR FROM CURRENT_DATE) AND EXTRACT(MONTH FROM date) = EXTRACT(MONTH FROM CURRENT_DATE) GROUP BY \"date\" ORDER BY date NULLS FIRST", "refId": "A" } ], @@ -710,7 +710,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT COALESCE(MAX(display_name), user_id) AS \"User\", MAX(subscription_tier) AS \"Tier\", ROUND(SUM(credits_used), 1) AS \"Total Credits Used\", MAX(date) AS \"Last Activity\" FROM lake._tool_kiro_user_report WHERE $__timeFilter(date) AND subscription_tier = 'POWER' GROUP BY \"user_id\" HAVING MAX(date) < NOW() - INTERVAL '14 DAY' ORDER BY MAX(date) NULLS FIRST", + "rawSql": "SELECT COALESCE(MAX(display_name), user_id) AS \"User\", MAX(subscription_tier) AS \"Tier\", ROUND(SUM(credits_used), 1) AS \"Total Credits Used\", MAX(date) AS \"Last Activity\" FROM _tool_kiro_user_report WHERE $__timeFilter(date) AND subscription_tier = 'POWER' GROUP BY \"user_id\" HAVING MAX(date) < NOW() - INTERVAL '14 DAY' ORDER BY MAX(date) NULLS FIRST", "refId": "A" } ], diff --git a/grafana/dashboards/postgresql/kiro_logging.json b/grafana/dashboards/postgresql/kiro_logging.json index c0b4396cf33..0e3f36b0b48 100644 --- a/grafana/dashboards/postgresql/kiro_logging.json +++ b/grafana/dashboards/postgresql/kiro_logging.json @@ -171,7 +171,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT LPAD(CAST(hour_of_day AS TEXT), 2, '0') AS \"Hour\", SUM(chat_count) AS \"Chat Events\", SUM(completion_count) AS \"Completion Events\" FROM (SELECT EXTRACT(HOUR FROM timestamp) AS hour_of_day, COUNT(*) AS chat_count, 0 AS completion_count FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY EXTRACT(HOUR FROM timestamp) UNION ALL SELECT EXTRACT(HOUR FROM timestamp) AS hour_of_day, 0 AS chat_count, COUNT(*) AS completion_count FROM lake._tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY EXTRACT(HOUR FROM timestamp)) AS combined GROUP BY hour_of_day ORDER BY hour_of_day NULLS FIRST", + "rawSql": "SELECT LPAD(CAST(hour_of_day AS TEXT), 2, '0') AS \"Hour\", SUM(chat_count) AS \"Chat Events\", SUM(completion_count) AS \"Completion Events\" FROM (SELECT EXTRACT(HOUR FROM timestamp) AS hour_of_day, COUNT(*) AS chat_count, 0 AS completion_count FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY EXTRACT(HOUR FROM timestamp) UNION ALL SELECT EXTRACT(HOUR FROM timestamp) AS hour_of_day, 0 AS chat_count, COUNT(*) AS completion_count FROM _tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY EXTRACT(HOUR FROM timestamp)) AS combined GROUP BY hour_of_day ORDER BY hour_of_day NULLS FIRST", "refId": "A" } ], @@ -245,7 +245,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT CASE WHEN chat_trigger_type = '' OR chat_trigger_type IS NULL THEN '(unknown)' ELSE chat_trigger_type END AS \"Trigger Type\", COUNT(*) AS \"Events\" FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY \"chat_trigger_type\" ORDER BY COUNT(*) DESC NULLS LAST", + "rawSql": "SELECT CASE WHEN chat_trigger_type = '' OR chat_trigger_type IS NULL THEN '(unknown)' ELSE chat_trigger_type END AS \"Trigger Type\", COUNT(*) AS \"Events\" FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY \"chat_trigger_type\" ORDER BY COUNT(*) DESC NULLS LAST", "refId": "A" } ], @@ -393,7 +393,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT CASE WHEN file_extension = '' THEN '(unknown)' ELSE file_extension END AS \"File Type\", COUNT(*) AS \"Completions\" FROM lake._tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY \"file_extension\" ORDER BY COUNT(*) DESC NULLS LAST LIMIT 15", + "rawSql": "SELECT CASE WHEN file_extension = '' THEN '(unknown)' ELSE file_extension END AS \"File Type\", COUNT(*) AS \"Completions\" FROM _tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY \"file_extension\" ORDER BY COUNT(*) DESC NULLS LAST LIMIT 15", "refId": "A" } ], @@ -589,7 +589,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "SELECT time, SUM(chat) AS \"Chat Events\", SUM(completions) AS \"Completion Events\" FROM (SELECT CAST(timestamp AS DATE) AS time, COUNT(*) AS chat, 0 AS completions FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE) UNION ALL SELECT CAST(timestamp AS DATE) AS time, 0 AS chat, COUNT(*) AS completions FROM lake._tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE)) AS combined GROUP BY time ORDER BY time NULLS FIRST", + "rawSql": "SELECT time, SUM(chat) AS \"Chat Events\", SUM(completions) AS \"Completion Events\" FROM (SELECT CAST(timestamp AS DATE) AS time, COUNT(*) AS chat, 0 AS completions FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE) UNION ALL SELECT CAST(timestamp AS DATE) AS time, 0 AS chat, COUNT(*) AS completions FROM _tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE)) AS combined GROUP BY time ORDER BY time NULLS FIRST", "refId": "A" } ], @@ -731,7 +731,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT SUM(CASE WHEN has_steering = TRUE THEN 1 ELSE 0 END) AS \"Using Steering\", SUM(CASE WHEN is_spec_mode = TRUE THEN 1 ELSE 0 END) AS \"Using Spec Mode\", SUM(CASE WHEN has_steering = FALSE AND is_spec_mode = FALSE THEN 1 ELSE 0 END) AS \"Plain Chat\" FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp)", + "rawSql": "SELECT SUM(CASE WHEN has_steering = TRUE THEN 1 ELSE 0 END) AS \"Using Steering\", SUM(CASE WHEN is_spec_mode = TRUE THEN 1 ELSE 0 END) AS \"Using Spec Mode\", SUM(CASE WHEN has_steering = FALSE AND is_spec_mode = FALSE THEN 1 ELSE 0 END) AS \"Plain Chat\" FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp)", "refId": "A" } ], @@ -977,7 +977,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "SELECT CAST(timestamp AS DATE) AS time, AVG(prompt_length) AS \"Avg Prompt Length\", AVG(response_length) AS \"Avg Response Length\", MAX(prompt_length) AS \"Max Prompt Length\", MAX(response_length) AS \"Max Response Length\" FROM lake._tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE) ORDER BY CAST(timestamp AS DATE) NULLS FIRST", + "rawSql": "SELECT CAST(timestamp AS DATE) AS time, AVG(prompt_length) AS \"Avg Prompt Length\", AVG(response_length) AS \"Avg Response Length\", MAX(prompt_length) AS \"Max Prompt Length\", MAX(response_length) AS \"Max Response Length\" FROM _tool_kiro_chat_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE) ORDER BY CAST(timestamp AS DATE) NULLS FIRST", "refId": "A" } ], @@ -1074,7 +1074,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "SELECT CAST(timestamp AS DATE) AS time, ROUND(AVG(left_context_length)) AS \"Avg Left Context\", ROUND(AVG(right_context_length)) AS \"Avg Right Context\", ROUND(AVG(left_context_length + right_context_length)) AS \"Avg Total Context\" FROM lake._tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE) ORDER BY CAST(timestamp AS DATE) NULLS FIRST", + "rawSql": "SELECT CAST(timestamp AS DATE) AS time, ROUND(AVG(left_context_length)) AS \"Avg Left Context\", ROUND(AVG(right_context_length)) AS \"Avg Right Context\", ROUND(AVG(left_context_length + right_context_length)) AS \"Avg Total Context\" FROM _tool_kiro_completion_log WHERE $__timeFilter(timestamp) GROUP BY CAST(timestamp AS DATE) ORDER BY CAST(timestamp AS DATE) NULLS FIRST", "refId": "A" } ], diff --git a/grafana/dashboards/postgresql/kiro_user_report.json b/grafana/dashboards/postgresql/kiro_user_report.json index 4f720f1aaf4..8f257a6a82e 100644 --- a/grafana/dashboards/postgresql/kiro_user_report.json +++ b/grafana/dashboards/postgresql/kiro_user_report.json @@ -76,7 +76,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT SUM(credits_used) AS \"Total Credits Used\", COUNT(DISTINCT user_id) AS \"Active Users\", SUM(total_messages) AS \"Total Messages\", SUM(chat_conversations) AS \"Total Conversations\" FROM lake._tool_kiro_user_report WHERE $__timeFilter(date)", + "rawSql": "SELECT SUM(credits_used) AS \"Total Credits Used\", COUNT(DISTINCT user_id) AS \"Active Users\", SUM(total_messages) AS \"Total Messages\", SUM(chat_conversations) AS \"Total Conversations\" FROM _tool_kiro_user_report WHERE $__timeFilter(date)", "refId": "A" } ], @@ -174,7 +174,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "SELECT date AS time, subscription_tier AS metric, SUM(credits_used) AS value FROM lake._tool_kiro_user_report WHERE $__timeFilter(date) GROUP BY \"date\", \"subscription_tier\" ORDER BY date NULLS FIRST", + "rawSql": "SELECT date AS time, subscription_tier AS metric, SUM(credits_used) AS value FROM _tool_kiro_user_report WHERE $__timeFilter(date) GROUP BY \"date\", \"subscription_tier\" ORDER BY date NULLS FIRST", "refId": "A" } ], @@ -346,7 +346,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT subscription_tier AS \"Tier\", COUNT(DISTINCT user_id) AS \"Users\" FROM lake._tool_kiro_user_report WHERE $__timeFilter(date) AND NOT subscription_tier IS NULL AND subscription_tier <> '' GROUP BY \"subscription_tier\" ORDER BY COUNT(DISTINCT user_id) DESC NULLS LAST", + "rawSql": "SELECT subscription_tier AS \"Tier\", COUNT(DISTINCT user_id) AS \"Users\" FROM _tool_kiro_user_report WHERE $__timeFilter(date) AND NOT subscription_tier IS NULL AND subscription_tier <> '' GROUP BY \"subscription_tier\" ORDER BY COUNT(DISTINCT user_id) DESC NULLS LAST", "refId": "A" } ], @@ -463,7 +463,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "SELECT COALESCE(MAX(display_name), user_id) AS \"User\", subscription_tier AS \"Tier\", client_type AS \"Client\", SUM(credits_used) AS \"Credits Used\", SUM(total_messages) AS \"Messages\", SUM(chat_conversations) AS \"Conversations\", SUM(overage_credits_used) AS \"Overage Credits\", CASE WHEN MAX(CAST(overage_enabled AS BIGINT)) = 1 THEN 'Yes' ELSE 'No' END AS \"Overage\", MIN(date) AS \"First Activity\", MAX(date) AS \"Last Activity\" FROM lake._tool_kiro_user_report WHERE $__timeFilter(date) GROUP BY \"user_id\", \"subscription_tier\", \"client_type\" ORDER BY user_id DESC NULLS LAST", + "rawSql": "SELECT COALESCE(MAX(display_name), user_id) AS \"User\", subscription_tier AS \"Tier\", client_type AS \"Client\", SUM(credits_used) AS \"Credits Used\", SUM(total_messages) AS \"Messages\", SUM(chat_conversations) AS \"Conversations\", SUM(overage_credits_used) AS \"Overage Credits\", CASE WHEN BOOL_OR(overage_enabled) THEN 'Yes' ELSE 'No' END AS \"Overage\", MIN(date) AS \"First Activity\", MAX(date) AS \"Last Activity\" FROM _tool_kiro_user_report WHERE $__timeFilter(date) GROUP BY \"user_id\", \"subscription_tier\", \"client_type\" ORDER BY user_id DESC NULLS LAST", "refId": "A" } ],