Skip to content

feat: add active days endpoint under v1-alpha - #2239

Merged
epipav merged 5 commits into
mainfrom
feat/IN-1334-active-days
Sep 21, 2026
Merged

epipav merged 5 commits into
mainfrom
feat/IN-1334-active-days

Conversation

@epipav

@epipav epipav commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

What

Adds GET /v1-alpha/projects/{slug}/development/active-days, the API port of the Active Days widget handler (frontend/server/api/widget/development/active-days.get.ts).

Query: repos[], startDate, endDate (UTC calendar days), granularity (required: daily, weekly, monthly, quarterly, yearly) and includeCollaborations (optional boolean, default false).

Response:

{
  "summary": { "current": 80, "previous": 64, "percentageChange": 25, "changeValue": 16,
               "periodFrom": "2025-01-01T00:00:00Z", "periodTo": "2025-03-31T00:00:00Z" },
  "avgContributionsPerDay": 21.5,
  "data": [
    { "startDate": "2025-01-01T00:00:00Z", "endDate": "2025-01-31T00:00:00Z", "contributions": 698 }
  ]
}

summary counts active days (days with at least one development activity) in the current period against the previous one, which has the same length and ends the day before startDate. data has one bucket per granularity step with the contributions in it.

Three concurrent calls to the active_days pipe (current summary, previous summary, series), as the Nuxt data layer makes them, with the slug sent as project.

Changes vs the Nuxt handler

  • day (the 1-based bucket index) is dropped.
  • avgContributions is renamed to avgContributionsPerDay, the pipe's own name.
  • An empty Tinybird result returns 200 with zeros and data: []. Nuxt reads currentSummary.data[0] unguarded and returns a 500.
  • percentageChange is signed (Nuxt used Math.abs).
  • Query dates are YYYY-MM-DD; response dates are ISO-8601 UTC; Tinybird gets YYYY-MM-DD 00:00:00 exactly as Nuxt's Luxon serializer sends it.

Decisions

  • includeCollaborations is exposed (optional boolean, default false) and passed to the pipe. The Nuxt handler hardcodes false, which is why the widget's collaboration toggle does nothing. The pipe accepts the flag: Nuxt's series call already sends includeCodeContributions=true&includeCollaborations=false (it spreads the whole filter), and ActiveDaysFilter declares both flags. Default false keeps the default response identical to today's widget. Same param as IN-1335.
  • The flags go on all three pipe calls, summaries included. Nuxt sends them only on the series call, so with the flag exposed the summary would disagree with the series whenever includeCollaborations=true. This follows the active_contributors data layer, which sends both flags on all three of its calls.
  • includeCodeContributions stays hardcoded to true, as in Nuxt. Exposing it later is additive.
  • granularity is required. Nuxt relies on a pipe default that is not visible from this repo; a required param keeps the series shape explicit instead of documenting a default we cannot verify.
  • An unknown slug returns 200 with zeros (epic definition of done for metric endpoints). The Tinybird client throws TinybirdProjectNotFoundError when the bucket lookup finds no row for the slug; the route catches exactly that class and treats the call as an empty result. A 404 from the pipe itself still maps to 503, and a test pins that.
  • A null avgContributionsPerDay becomes 0. The column is Nullable(Float64) and is null only with zero active days, which the empty case already reports as 0.
  • A bare repos= is dropped rather than sent to the pipe as a filter on an empty repository name (Ajv coerces it to ['']), matching the Nuxt handler.
  • Every Tinybird failure (any status, network error, non-JSON body) maps to 503 upstream_unavailable, logged once per request. A missing API_TB_* variable stays a 500, as on /projects/{slug}. Cache-Control is inherited from the alpha plugin. OpenAPI tag Development; every response field and added query param carries a description with its unit.

Tests

api/tests/development-active-days.test.ts: 32 cases through buildApp() with fetch stubbed at the HTTP boundary, so the real Tinybird client builds the URLs, resolves the bucket and classifies the errors. Covers the response shape and key set, the exact params of the three pipe calls and their concurrency, the includeCollaborations pass-through and validation, empty repos, signed and null percentageChange, empty results, unknown slug, date and granularity validation, the 503 mapping (pipe 500/404/401/429, bucket lookup 500, network error, non-JSON), Cache-Control, and the OpenAPI spec (tag, field descriptions, query params, absent from /v1).

From api/: pnpm test (14 files, 194 tests), pnpm tsc-check, pnpm lint, pnpm format:check all pass. No new runtime dependency.

Includes the routing-test parser fix from the base branch (62b136b).

Local helpers to fold into a shared module once the five Development PRs land: toTinybirdRange, toUtcDateTime (same form as period.ts's private utcMidnight), the fetchRows wrapper (bucket-miss to empty rows) with the .catch 503 mapping, and the Type.Composite([DateRangeQuery, ...]) query schema. The api's Tinybird client has no bucket cache, so each request makes three project_buckets lookups for the same slug; wiring the client's in-process cache fixes that for every route.

Based on main, which includes #2241 (IN-1348, directory-based route registration) as dd37a7a. Jira: IN-1334

Copilot AI balanced review requested due to automatic review settings September 21, 2026 11:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The previous-period calculation can violate the documented equal-length contract, and two smaller documentation and test-reliability issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
What changed in this PR

Adds the v1-alpha Active Days development metric endpoint backed by Tinybird.

Changes:

  • Registers GET /v1-alpha/projects/{slug}/development/active-days.
  • Adds response mapping, validation, error handling, and OpenAPI schemas.
  • Adds comprehensive integration tests.
File Description
api/​src/​versions/​v1-alpha/​index.ts Registers the endpoint.
api/​src/​versions/​v1-alpha/​development/​active-days.ts Implements the route and schemas.
api/​tests/​development-active-days.test.ts Tests behavior, failures, and OpenAPI output.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread api/src/versions/v1-alpha/development/active-days.ts
Comment thread api/tests/development-active-days.test.ts
Comment thread api/src/versions/v1-alpha/development/active-days.ts Outdated
Copilot AI review requested due to automatic review settings September 21, 2026 11:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The implementation is consistent with existing API patterns and has comprehensive integration coverage.

Review effort: Balanced
Findings: None

Resolved since last review (3)

@epipav epipav self-assigned this Sep 21, 2026

@gaspergrom gaspergrom left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Checked the includeCollaborations fan-out across all three pipe calls and the TinybirdProjectNotFoundError catch, both hold up, and the null avgContributionsPerDay path lands on 0. Tests cover the edge cases well. LGTM.

Base automatically changed from feat/IN-1345-project-endpoint to main September 21, 2026 12:26
@epipav
epipav force-pushed the feat/IN-1334-active-days branch from 1a8ae90 to 79c89ae Compare September 21, 2026 12:30
Copilot AI review requested due to automatic review settings September 21, 2026 12:40
@epipav
epipav force-pushed the feat/IN-1334-active-days branch from 79c89ae to 8207332 Compare September 21, 2026 12:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The handler performs three redundant bucket-resolution requests for every endpoint call.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Comment thread api/src/versions/v1-alpha/development/active-days.ts Outdated

@themarolt themarolt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm - one question on the nullable bucket dates in the series

Comment thread api/src/versions/v1-alpha/development/active-days.ts Outdated
Copilot AI review requested due to automatic review settings September 21, 2026 12:55
@epipav
epipav force-pushed the feat/IN-1334-active-days branch from 8207332 to 8c01f4f Compare September 21, 2026 12:55
@epipav
epipav changed the base branch from main to refactor/IN-1348-autoload-routes September 21, 2026 12:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The endpoint performs three redundant unthrottled project-bucket lookups per request, increasing upstream load and failure exposure.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Base automatically changed from refactor/IN-1348-autoload-routes to main September 21, 2026 14:16
Signed-off-by: anilb <epipav@gmail.com>
Signed-off-by: anilb <epipav@gmail.com>
Copilot AI review requested due to automatic review settings September 21, 2026 14:18
@epipav
epipav force-pushed the feat/IN-1334-active-days branch from 8c01f4f to df2ce44 Compare September 21, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The implementation matches its documented contract, and the previously reported issues are resolved with comprehensive test coverage.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Signed-off-by: anilb <epipav@gmail.com>
Copilot AI review requested due to automatic review settings September 21, 2026 14:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Concurrent upstream failures can produce multiple route-level error logs despite the stated once-per-request behavior.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread api/src/versions/v1-alpha/development/active-days.ts
Copilot AI review requested due to automatic review settings September 21, 2026 15:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Malformed but valid JSON Tinybird responses can escape the 503 mapping and produce a 500.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread api/src/versions/v1-alpha/development/active-days.ts
Copilot AI review requested due to automatic review settings September 21, 2026 15:09
@epipav
epipav merged commit dfd5f4d into main Sep 21, 2026
11 checks passed
@epipav
epipav deleted the feat/IN-1334-active-days branch September 21, 2026 15:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Malformed Tinybird data can bypass the 503 mapping and produce an incorrect zero response or an internal 500.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment on lines +64 to +66
current: Type.Number({ description: 'Active days in the current period (count of days).' }),
previous: Type.Number({ description: 'Active days in the previous period (count of days).' }),
changeValue: Type.Number({ description: 'current minus previous (count of days).' }),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants