Skip to content

fix(server): default missing entry_timestamp for non-daily custom measurements - #2091

Merged
CodeWithCJ merged 3 commits into
mainfrom
dev
Aug 10, 2026
Merged

fix(server): default missing entry_timestamp for non-daily custom measurements#2091
CodeWithCJ merged 3 commits into
mainfrom
dev

Conversation

@CodeWithCJ

@CodeWithCJ CodeWithCJ commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Fixes #2074 where logging custom measurement categories with non-Daily frequencies (e.g. Hourly, All, Unlimited) failed with a Postgres NOT NULL constraint error on custom_measurements.entry_timestamp when omitted by callers like log_custom_metric in AI chat.

  • Added defaultEntryTimestamp helper using @workspace/shared isDayString and Date.UTC date construction to prevent timezone jump issues.
  • When entry_timestamp is omitted and entry_date is today, defaults to new Date().toISOString() to preserve the exact hour, minute, and second of entry.
  • Updated upsertCustomMeasurement and bulkUpsertCustomMeasurements in measurementRepository.ts.
  • Added unit tests in measurementRepository.customMetricTimestamp.test.ts and chatbotToolsCheckin.test.ts.

Tip

Help us review and merge your PR faster!
Please ensure you have completed the Checklist below.
For Frontend changes, please run pnpm run validate to check for any errors.
PRs that include tests and clear screenshots are highly preferred!
Note: AI-generated descriptions must be manually edited for conciseness. Do not paste raw AI summaries.

Description

What problem does this PR solve?
(Keep it concise. 1–2 sentences.)

How did you implement the solution?
(Brief technical approach.)

Linked Issue: Closes #2074

How to Test

  1. Check out this branch and run ...
  2. Navigate to...
  3. Verify that...

PR Type

  • Issue (bug fix)
  • New Feature
  • Refactor
  • Documentation

Checklist

All PRs:

  • [MANDATORY - ALL] Integrity & License: I certify this is my own work, free of malicious code, and I agree to the License terms.

New features only:

  • [MANDATORY for new feature] Alignment: I have raised a GitHub issue and it was reviewed/approved by maintainers or it was approved on Discord.

Frontend changes (SparkyFitnessFrontend/):

  • [MANDATORY for Frontend changes] Quality: I have run pnpm run validate and it passes.
  • [MANDATORY for Frontend changes] Translations: I have only updated the English (en) translation file.

Backend changes (SparkyFitnessServer/):

  • [MANDATORY for Backend changes] Code Quality: I have run typecheck, lint, and tests. New files use TypeScript, new endpoints have Zod schemas, and new endpoints include tests.
  • [MANDATORY for Backend changes] Database Security: I have updated rls_policies.sql for any new user-specific tables.

UI changes (components, screens, pages):

  • [MANDATORY for UI changes] Screenshots: I have attached Before/After screenshots below.

Mobile changes (SparkyFitnessMobile/):

  • [MANDATORY for Mobile changes] Tested on device or emulator: I have verified the changes work on iOS or Android.

Screenshots

Click to expand

Before

before

After

after

Notes for Reviewers

Optional — use this for anything that doesn't fit above: known tradeoffs, areas you'd like specific feedback on, qustions you have or context that helps reviewers.

Summary by CodeRabbit

  • Bug Fixes

    • Improved timestamp handling for custom measurements across daily, hourly, and unlimited entries.
    • Applied timezone-aware date and time handling, including safe fallbacks for missing or invalid values.
    • Ensured consistent behavior for individual and bulk measurement updates.
    • Preserved measurement sources and normalized missing notes consistently.
  • Tests

    • Added coverage for timezone-aware timestamp defaults and bulk updates.
    • Added coverage confirming successful logging of unlimited-frequency custom metrics.

…surements

Fixes #2074 where logging custom measurement categories with non-Daily frequencies (e.g. Hourly, All, Unlimited) failed with a Postgres NOT NULL constraint error on custom_measurements.entry_timestamp when omitted by callers like log_custom_metric in AI chat.

- Added defaultEntryTimestamp helper using @workspace/shared isDayString and Date.UTC date construction to prevent timezone jump issues.
- When entry_timestamp is omitted and entry_date is today, defaults to new Date().toISOString() to preserve the exact hour, minute, and second of entry.
- Updated upsertCustomMeasurement and bulkUpsertCustomMeasurements in measurementRepository.ts.
- Added unit tests in measurementRepository.customMetricTimestamp.test.ts and chatbotToolsCheckin.test.ts.
@github-actions github-actions Bot added backend bug Something isn't working labels Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

PR Validation Results

Change Detection

  • ⚙️ Backend changes detected

✅ All checks passed. Thank you!

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fe54a121-9c0b-4489-88af-a4dc450ed8eb

📥 Commits

Reviewing files that changed from the base of the PR and between 495af5d and 6983921.

📒 Files selected for processing (3)
  • SparkyFitnessServer/models/measurementRepository.ts
  • SparkyFitnessServer/services/measurementService.ts
  • SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts

📝 Walkthrough

Walkthrough

Custom measurement upserts now apply UTC-aware timestamp defaults for daily, non-daily, single, and bulk entries. The service passes timezone data to the repository. Tests cover repository defaults and successful log_custom_metric execution.

Changes

Custom measurement timestamp handling

Layer / File(s) Summary
Timestamp normalization and typed upsert contracts
SparkyFitnessServer/models/measurementRepository.ts
Adds timestamp defaulting for supplied, derived, current-day, invalid, and missing dates. Applies the behavior to single and bulk upserts. Adds typed bulk inputs and result handling. Normalizes missing notes to null.
Service timezone propagation
SparkyFitnessServer/services/measurementService.ts
Types the custom measurement payload. Resolves the timezone from the payload or account settings. Passes the timezone and measurement source to the repository.
Timestamp defaulting validation
SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts, SparkyFitnessServer/tests/chatbotToolsCheckin.test.ts
Tests timezone-specific Unlimited defaults, Hourly date-and-hour defaults, bulk defaults, and successful custom-metric logging.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CheckinTools
  participant MeasurementService
  participant MeasurementRepository
  participant Database
  CheckinTools->>MeasurementService: log_custom_metric payload
  MeasurementService->>MeasurementService: resolve payload or account timezone
  MeasurementService->>MeasurementRepository: upsert custom measurement with source and timezone
  MeasurementRepository->>MeasurementRepository: default entry timestamp
  MeasurementRepository->>Database: insert or update custom measurement
  Database-->>MeasurementRepository: persisted row
  MeasurementRepository-->>MeasurementService: upsert result
Loading

Possibly related PRs

Suggested reviewers: apedley

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes provide timestamp fallbacks in single and bulk upserts and add tests for the missing entry_timestamp failure described in issue #2074.
Out of Scope Changes check ✅ Passed The repository, service, and test changes directly support the timestamp fallback objective and contain no unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the primary change: adding default entry timestamps for non-daily custom measurements.
Description check ✅ Passed The description explains the problem, implementation, linked issue, tests, and change type, but the How to Test section still contains placeholders.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts (1)

25-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the current-day fallback test deterministic.

Line 40 uses the wall clock. Lines 59-61 only verify that the value is parseable. A UTC-midnight fallback would pass this test even though the PR requires the current timestamp for today.

Set a fixed clock. Use a fixed entryDate. Assert the exact value passed as parameter $6.

As per coding guidelines, avoid toISOString().split('T')[0] for business-logic dates and keep calendar days as YYYY-MM-DD strings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts`
around lines 25 - 61, Update the test case around upsertCustomMeasurement to use
a fixed system clock and a fixed YYYY-MM-DD entryDate string instead of deriving
today from toISOString().split('T')[0]. Assert that values[5] equals the exact
expected current timestamp produced by the fixed clock, replacing the
parseability-only checks while preserving the existing query assertions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@SparkyFitnessServer/models/measurementRepository.ts`:
- Around line 35-38: The current-time fallback incorrectly compares entryDate
against the UTC day; update the shared measurement upsert logic used by both
single-row and bulk operations to compare it with todayInZone(userTimezone),
while preserving the existing now timestamp return. Add coverage for matching
and non-matching dates across positive and negative timezone offsets around UTC
midnight.

In
`@SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts`:
- Around line 10-11: Replace the any-typed mockClient in the measurement
repository tests with the existing MockDbClient helper, and type query-call
values as unknown[] or an explicit query-call tuple. Remove the associated
`@typescript-eslint/no-explicit-any` suppressions while preserving the existing
test behavior.

---

Nitpick comments:
In
`@SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts`:
- Around line 25-61: Update the test case around upsertCustomMeasurement to use
a fixed system clock and a fixed YYYY-MM-DD entryDate string instead of deriving
today from toISOString().split('T')[0]. Assert that values[5] equals the exact
expected current timestamp produced by the fixed clock, replacing the
parseability-only checks while preserving the existing query assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 77ffcb17-ffbe-452a-9894-572b5dab13e5

📥 Commits

Reviewing files that changed from the base of the PR and between cb00282 and ac3a90b.

📒 Files selected for processing (3)
  • SparkyFitnessServer/models/measurementRepository.ts
  • SparkyFitnessServer/tests/chatbotToolsCheckin.test.ts
  • SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts

Comment thread SparkyFitnessServer/models/measurementRepository.ts
Comment thread SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts Outdated
@CodeWithCJ

Copy link
Copy Markdown
Owner Author

@CodeRabbit can you review again.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@CodeWithCJ I will review the current changes in #2091.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@SparkyFitnessServer/models/measurementRepository.ts`:
- Around line 1151-1154: The custom-measurement paths still use explicit any
types and ESLint suppressions. In
SparkyFitnessServer/models/measurementRepository.ts around lines 1151-1154, type
database rows, existingByKey, writtenByInput, and update results; in
SparkyFitnessServer/services/measurementService.ts around lines 1651-1652,
define a payload type, type user identifiers, and replace the category-lookup
callback’s any parameter. Remove the related suppressions while preserving
existing behavior.

In `@SparkyFitnessServer/services/measurementService.ts`:
- Around line 1651-1652: Update the timezone selection near the current-day
timestamp fallback to accept payload.timezone only when
isValidTimeZone(payload.timezone) returns true; otherwise call
loadUserTimezone(authenticatedUserId). Preserve the existing authenticated-user
fallback and ensure invalid or absent payload values never reach the timestamp
logic.

In
`@SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts`:
- Around line 73-127: Make the timezone-boundary tests deterministic by using
fake timers and freezing Tokyo to 2026-08-08T23:30:00.000Z and Los Angeles to
2026-08-08T00:30:00.000Z. In both upsertCustomMeasurement tests, assert the
captured entry_timestamp parameter equals the corresponding frozen ISO timestamp
instead of only checking date validity, and restore real timers after each test.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dede3100-2ced-43c9-978a-394c4ff2d97e

📥 Commits

Reviewing files that changed from the base of the PR and between ac3a90b and 495af5d.

📒 Files selected for processing (3)
  • SparkyFitnessServer/models/measurementRepository.ts
  • SparkyFitnessServer/services/measurementService.ts
  • SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts

Comment thread SparkyFitnessServer/models/measurementRepository.ts
Comment thread SparkyFitnessServer/services/measurementService.ts Outdated
Comment thread SparkyFitnessServer/tests/measurementRepository.customMetricTimestamp.test.ts Outdated
@CodeWithCJ
CodeWithCJ merged commit 77a9308 into main Aug 10, 2026
9 of 10 checks passed
@CodeWithCJ
CodeWithCJ deleted the dev branch August 13, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom measurement logging via AI chat fails - missing entry_timestamp (log_custom_metric)

1 participant