Skip to content

feature: Add claude code metrics data integration#8808

Open
la-tamas wants to merge 1 commit intoapache:mainfrom
archfz:feat/team_users_ui
Open

feature: Add claude code metrics data integration#8808
la-tamas wants to merge 1 commit intoapache:mainfrom
archfz:feat/team_users_ui

Conversation

@la-tamas
Copy link
Copy Markdown

⚠️ Pre Checklist

Please complete ALL items in this checklist, and remove before submitting

  • I have read through the Contributing Documentation.
  • I have added relevant tests.
  • I have added relevant documentation.
  • I will add labels to the PR, such as pr-type/bug-fix, pr-type/feature-development, etc.

Summary

The PR allows users to:

  • configure connection to Claude Code Analytics API
  • ingest data from the API
  • display the data in dashboards.

Does this close any open issues?

None

Screenshots

Include any relevant screenshots here.

Other Information

Any other information that is important to this PR.

(cherry picked from commit 78fd47d9e7647ff3ac8b886b2ddb45f1df55e2b4)
(cherry picked from commit fa69cf9ec0fa0db6aa5fb0aec2e5ca2796355ffd)

(upstream) story: XPL-551: Add license headers

(cherry picked from commit a12ece8c1ca65ff27acc14d4630ac3d404d51ce8)
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. add-a-plugin This issue is to add a plugin component/plugins This issue or PR relates to plugins pr-type/feature-development This PR is to develop a new feature labels Mar 24, 2026
@la-tamas la-tamas changed the title feat: Add claude code metrics data integration feature: Add claude code metrics data integration Mar 24, 2026
@ewega ewega requested a review from Copilot March 24, 2026 19:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Claude Code data source integration to DevLake, including backend collectors/extractors for Anthropic analytics metrics, Config UI connection/scope support, and Grafana dashboards to visualize adoption.

Changes:

  • Introduces a new claude_code backend plugin (models, migrations, API endpoints, pipeline plan, and analytics collectors/extractors).
  • Extends Config UI to register/configure the Claude Code plugin, including token/org/custom-header connection fields.
  • Adds two Grafana dashboards for org/user adoption views based on the new tool-layer tables.

Reviewed changes

Copilot reviewed 61 out of 62 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
grafana/dashboards/ClaudeCodeAdoption.json New org-level Claude Code adoption dashboard querying _tool_claude_code_* tables.
grafana/dashboards/ClaudeCodeAdoptionByUser.json New user-focused dashboard with row-level access filtering patterns in SQL.
config-ui/src/types/connection.ts Extends connection typings to include custom headers and org/admin key fields.
config-ui/src/plugins/utils.ts Adds claude_code scope naming logic and plugin alias mapping.
config-ui/src/plugins/register/index.ts Registers the Claude Code plugin in the UI plugin list.
config-ui/src/plugins/register/claude-code/index.ts Exports Claude Code plugin UI config module.
config-ui/src/plugins/register/claude-code/config.tsx Defines Claude Code UI config: connection defaults, fields, entities.
config-ui/src/plugins/register/claude-code/assets/icon.svg Adds Claude Code plugin icon.
config-ui/src/plugins/register/claude-code/connection-fields/index.ts Exposes Claude Code connection field components.
config-ui/src/plugins/register/claude-code/connection-fields/organization.tsx UI field to collect Anthropic organization identifier.
config-ui/src/plugins/register/claude-code/connection-fields/token.tsx UI field to collect Anthropic API key (token) with conditional requirement.
config-ui/src/plugins/register/claude-code/connection-fields/custom-headers.tsx UI to configure custom auth headers (middleware/proxy scenarios).
config-ui/src/plugins/register/claude-code/connection-fields/styled.ts Shared styling for Claude Code connection field validation text.
config-ui/src/plugins/components/connection-form/index.tsx Ensures connection test payload includes rateLimitPerHour and customHeaders.
config-ui/src/features/connections/utils.ts Simplifies connection transformation by spreading API payload into UI model.
config-ui/src/config/entities.ts Adds CLAUDE_CODE entity label for UI display.
config-ui/src/api/connection/index.ts Extends connection test payload typing to include new connection fields.
backend/plugins/claude_code/claude_code.go Adds standalone runner entry for debugging the plugin.
backend/plugins/claude_code/impl/impl.go Implements plugin interfaces: init, API resources, models, subtasks, pipeline plan, migrations.
backend/plugins/claude_code/impl/connection_helper.go Normalization helper for Claude Code connections.
backend/plugins/claude_code/api/init.go Initializes API helpers for connections/scopes/scope-configs and remote scope listing.
backend/plugins/claude_code/api/connection.go Connection CRUD + validation for Claude Code connections.
backend/plugins/claude_code/api/test_connection.go Test connection endpoints (new + existing connection override).
backend/plugins/claude_code/api/connection_test.go Unit tests for connection validation rules.
backend/plugins/claude_code/api/remote_api.go Remote scope listing/search implementation (organization as scope).
backend/plugins/claude_code/api/scope.go Scope CRUD endpoints via shared DS helper.
backend/plugins/claude_code/api/scope_config.go Scope config CRUD endpoints via shared DS helper.
backend/plugins/claude_code/api/blueprint_v200.go Generates blueprint v200 pipeline plan for Claude Code scopes.
backend/plugins/claude_code/service/connection_test_helper.go Implements HTTP-level connection test logic against Anthropic endpoints.
backend/plugins/claude_code/models/models.go Registers tool-layer tables for the plugin.
backend/plugins/claude_code/models/connection.go Defines connection model, auth header setup, sanitization, and PATCH merge behavior.
backend/plugins/claude_code/models/connection_test.go Tests secret-preserving merge behavior for token/custom headers.
backend/plugins/claude_code/models/scope.go Defines organization scope model and normalization in BeforeSave.
backend/plugins/claude_code/models/scope_config.go Defines tool-layer scope config model.
backend/plugins/claude_code/models/activity_summary.go Tool-layer model for org activity summary metrics.
backend/plugins/claude_code/models/user_activity.go Tool-layer model for per-user daily analytics metrics.
backend/plugins/claude_code/models/chat_project.go Tool-layer model for chat project usage metrics.
backend/plugins/claude_code/models/skill_usage.go Tool-layer model for skill usage metrics.
backend/plugins/claude_code/models/connector_usage.go Tool-layer model for connector usage metrics.
backend/plugins/claude_code/models/migrationscripts/register.go Registers Claude Code plugin migration scripts.
backend/plugins/claude_code/models/migrationscripts/20260309_initialize.go Creates initial Claude Code connection/scope/scope-config tables.
backend/plugins/claude_code/models/migrationscripts/20260319_add_custom_headers.go Adds custom headers support to connection table schema.
backend/plugins/claude_code/models/migrationscripts/20260319_replace_analytics_tables.go Adds new analytics tool-layer + raw tables for the updated endpoints.
backend/plugins/claude_code/tasks/options.go Defines task options (connectionId, scopeId).
backend/plugins/claude_code/tasks/task_data.go Defines runtime task data container passed through subtasks.
backend/plugins/claude_code/tasks/subtasks.go Declares subtask metas for collecting/extracting analytics datasets.
backend/plugins/claude_code/tasks/register.go Returns the ordered list of Claude Code subtask metas.
backend/plugins/claude_code/tasks/api_client.go Creates API client with rate limiting and Retry-After handling.
backend/plugins/claude_code/tasks/retry_after.go Parses HTTP Retry-After header into a duration.
backend/plugins/claude_code/tasks/collector_utils.go Shared collectors utilities: date windows, iterators, pagination parsing.
backend/plugins/claude_code/tasks/collector_utils_test.go Unit tests for date-range computation logic.
backend/plugins/claude_code/tasks/activity_summary_collector.go Collector for org summaries endpoint (chunked date ranges).
backend/plugins/claude_code/tasks/activity_summary_extractor.go Extractor for org summary records into tool-layer table.
backend/plugins/claude_code/tasks/user_activity_collector.go Collector for per-user daily analytics endpoint.
backend/plugins/claude_code/tasks/user_activity_extractor.go Extractor for per-user activity records into tool-layer table.
backend/plugins/claude_code/tasks/user_activity_extractor_test.go Unit tests around user activity email normalization/filter helpers.
backend/plugins/claude_code/tasks/chat_project_collector.go Collector for chat project usage endpoint.
backend/plugins/claude_code/tasks/chat_project_extractor.go Extractor for chat project usage records into tool-layer table.
backend/plugins/claude_code/tasks/skill_usage_collector.go Collector for skill usage endpoint.
backend/plugins/claude_code/tasks/skill_usage_extractor.go Extractor for skill usage records into tool-layer table.
backend/plugins/claude_code/tasks/connector_usage_collector.go Collector for connector usage endpoint.
backend/plugins/claude_code/tasks/connector_usage_extractor.go Extractor for connector usage records into tool-layer table.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +45
const hasCustomHeaders = (values.customHeaders ?? []).length > 0;

const error = useMemo(() => {
if (type === 'update') return '';
if (hasCustomHeaders) return '';
return values.token?.trim() ? '' : 'Anthropic API Key is required (unless custom headers are configured)';
}, [type, values.token, hasCustomHeaders]);
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasCustomHeaders is currently based only on customHeaders.length > 0, so users can bypass the required API key by adding an empty custom header row. This matches the backend behavior (it only checks slice length) and can lead to saving an unauthenticated connection. Consider computing hasCustomHeaders based on at least one header with a non-empty key/value (and setting an error if headers are present but invalid).

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +41
const headers: Array<{ key: string; value: string }> = values.customHeaders ?? [];

useEffect(() => {
setValues({ customHeaders: initialValues.customHeaders ?? [] });
}, [type, initialValues.customHeaders]);

const addHeader = () => {
setValues({ customHeaders: [...headers, { key: '', value: '' }] });
};
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UI allows adding blank custom header entries ({ key: '', value: '' }). Because token-required logic and backend validation currently treat any non-empty header array as sufficient, this can accidentally create connections that have neither a valid token nor any effective auth headers. Consider preventing add/save when header key/value are empty, and/or filtering out empty entries before persisting.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +37
// replaceClaudeCodeAnalyticsTables drops the old deprecated endpoint tables and
// creates the five new analytics endpoint tables.
type replaceClaudeCodeAnalyticsTables struct{}

func (*replaceClaudeCodeAnalyticsTables) Up(basicRes context.BasicRes) errors.Error {
return migrationhelper.AutoMigrateTables(
basicRes,
&ccUserActivity20260319{},
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says this migration "drops the old deprecated endpoint tables", but Up only calls AutoMigrateTables (which typically creates/updates tables but does not drop old ones). If dropping is required to avoid stale tables/data, add explicit drop/cleanup logic; otherwise update the comment to reflect what the migration actually does.

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +105
hasToken := strings.TrimSpace(connection.Token) != ""
hasCustomHeaders := len(connection.CustomHeaders) > 0
if !hasToken && !hasCustomHeaders {
return errors.BadInput.New("either token or at least one custom header is required")
}
if strings.TrimSpace(connection.Organization) == "" {
return errors.BadInput.New("organization is required")
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateConnection treats any non-empty CustomHeaders slice as valid authentication. This allows saving/testing a connection with only blank header entries (e.g., {key:'', value:''}), which will not set any headers in SetupAuthentication and will lead to auth failures at runtime. Consider validating that at least one custom header has a non-empty key (and likely a non-empty value) before accepting it as an auth alternative to Token.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +67
// Use today's date for the test request.
today := time.Now().UTC().Format("2006-01-02")
endpoint := fmt.Sprintf("v1/organizations/usage_report/claude_code?starting_at=%s&limit=1", today)
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connection test uses today's date (time.Now().UTC()) even though collectors explicitly assume the Analytics API requires data to be at least a few days old (see claudeCodeAvailabilityLagDays usage in tasks). This can make the test endpoint fail for otherwise-correct credentials. Use a date that is guaranteed to be outside the availability lag window (e.g., now minus the lag days) for the test request.

Suggested change
// Use today's date for the test request.
today := time.Now().UTC().Format("2006-01-02")
endpoint := fmt.Sprintf("v1/organizations/usage_report/claude_code?starting_at=%s&limit=1", today)
// Use a date safely outside the analytics availability lag window for the test request.
testDate := time.Now().UTC().AddDate(0, 0, -7).Format("2006-01-02")
endpoint := fmt.Sprintf("v1/organizations/usage_report/claude_code?starting_at=%s&limit=1", testDate)

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +70
// Use today's date for the test request.
today := time.Now().UTC().Format("2006-01-02")
endpoint := fmt.Sprintf("v1/organizations/usage_report/claude_code?starting_at=%s&limit=1", today)

res, err := apiClient.Get(endpoint, nil, nil)
if err != nil {
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test request path (v1/organizations/usage_report/claude_code) does not match the analytics endpoints used by the collectors in this PR (v1/organizations/analytics/...). If usage_report/claude_code is deprecated/removed, connection tests will fail even though collection would work (or vice versa). Align the test endpoint with the same API surface the collectors use (e.g., a minimal analytics/users request with limit=1).

Suggested change
// Use today's date for the test request.
today := time.Now().UTC().Format("2006-01-02")
endpoint := fmt.Sprintf("v1/organizations/usage_report/claude_code?starting_at=%s&limit=1", today)
res, err := apiClient.Get(endpoint, nil, nil)
if err != nil {
// Use a minimal analytics users request with limit=1 for the test.
endpoint := "v1/organizations/analytics/users?limit=1"
res, err := apiClient.Get(endpoint, nil, nil)
if err != nil {
if err != nil {

Copilot uses AI. Check for mistakes.
return nil, errors.BadInput.New("either token or at least one custom header is required")
}
if strings.TrimSpace(connection.Organization) == "" {
return nil, errors.BadInput.New("organizationId is required")
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message says organizationId is required, but the connection field in this plugin/model is organization (and the API-side validateConnection returns "organization is required"). This inconsistency can confuse users and makes it harder to map UI fields to backend validation. Use consistent naming across validation and responses (either standardize on organization everywhere or explicitly rename the field/JSON tag to organizationId).

Suggested change
return nil, errors.BadInput.New("organizationId is required")
return nil, errors.BadInput.New("organization is required")

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

add-a-plugin This issue is to add a plugin component/plugins This issue or PR relates to plugins pr-type/feature-development This PR is to develop a new feature size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants