sleep-logger

Sleep Logger

Quality Gate Status Lines of Code Coverage Duplicated Lines (%)

Bugs Vulnerabilities Code Smells Technical Debt

Reliability Rating Security Rating Maintainability Rating

A REST API for tracking sleep habits, built with Kotlin, Spring Boot, and PostgreSQL. Users can log nightly sleep data, retrieve today’s entry, and view 30-day aggregate statistics including average bed/wake times and mood frequency breakdowns.

Tech Stack

Getting Started

Prerequisites

Run with Docker

docker compose up --build

This starts PostgreSQL on port 5432 and the API on port 8080. Flyway runs migrations automatically on startup.

Run locally

cd sleep
./gradlew bootRun

Requires a PostgreSQL instance at localhost:5432 (or configure via SPRING_DATASOURCE_URL).

API

The full API spec is rendered via Swagger UI on GitHub Pages: https://philipmvitale.github.io/sleep-logger/swagger

The source contract lives in sleep/src/main/resources/openapi/sleep-api.yaml. A simple bash client wrapping cURL (scripts/sleep-client.sh) is provided for quick manual testing. A postman collection is also available at resources/Sleep API.postman_collection.json for testing.

All endpoints require an X-User-Id header to identify the user.

The user id MUST belong to a user in the users table. A script (scripts/seed-db.sh) provides a user with ID 1 with 30 days of data in America/New_York, and a user with ID 2 with no data in America/Los_Angeles that you can use to test the API.

The user’s default timezone at the time of creating a record (not the offset in the provided ISO-8601 timestamp) is used to persist the time zone / offset needed for local time calculations as all times are stored in UTC.

There is no API to create or modify users, but you can modify the users table directly in the database to use different time zones.

Endpoints

Method Path Description
POST /api/v1/sleep-log Log last night’s sleep
GET /api/v1/sleep-log Get today’s sleep log
GET /api/v1/sleep-stats Get 30-day sleep statistics

Create a sleep log

curl -X POST http://localhost:8080/api/v1/sleep-log \
  -H "Content-Type: application/json" \
  -H "X-User-Id: 1" \
  -d '{
    "bedTime": "2024-01-14T22:30:00Z",
    "wakeTime": "2024-01-15T06:45:00Z",
    "mood": "GOOD"
  }'

Response (201):

{
  "bedTime": "2024-01-14T22:30:00Z",
  "bedTimeZone": "UTC",
  "wakeTime": "2024-01-15T06:45:00Z",
  "wakeTimeZone": "UTC",
  "durationMinutes": 495,
  "mood": "GOOD"
}

Get today’s sleep log

curl http://localhost:8080/api/v1/sleep-log -H "X-User-Id: 1"

Get 30-day statistics

curl http://localhost:8080/api/v1/sleep-stats -H "X-User-Id: 1"

Response (200):

{
  "dateFrom": "2023-12-17T00:00:00Z",
  "dateTo": "2024-01-15T00:00:00Z",
  "averageDurationMinutes": 465,
  "averageBedTime": "23:00:00",
  "averageWakeTime": "06:45:00",
  "moodFrequencies": {
    "badFrequency": 5,
    "okFrequency": 12,
    "goodFrequency": 13
  }
}

Mood values: BAD, OK, GOOD

CLI test script

A bash script is included for quick manual testing:

# Seed DB
./scripts/seed-db.sh

# Get today's log
./scripts/sleep-client.sh today

# Get 30-day stats
./scripts/sleep-client.sh stats

# Log sleep
./scripts/sleep-client.sh log 2024-01-14T22:30:00Z 2024-01-15T06:45:00Z GOOD

# Specify a different user
./scripts/sleep-client.sh -u 2 today

# Un-seed DB
./scripts/unseed-db.sh

Configure via environment variables: SLEEP_API_URL (default http://localhost:8080), SLEEP_USER_ID (default 1).

Project Structure

sleep/
  src/
    main/
      kotlin/.../sleep/
        controller/        # REST controllers (implement generated API interfaces)
        service/           # Business logic and validation
        repository/        # SQL queries via NamedParameterJdbcTemplate
        model/             # Domain models
        exception/         # Domain exceptions and DB constraint mapping
        configuration/     # Spring configuration (database)
      resources/
        openapi/           # API contract (sleep-api.yaml)
        db/migration/      # Flyway SQL migrations
    test/                  # Unit tests (MockK, no database)
    it/                    # Integration tests (Testcontainers + PostgreSQL)

The API contract lives in sleep-api.yaml. The OpenAPI Generator plugin produces request/response DTOs and controller interfaces at build time – controllers implement these generated interfaces directly.

Build & Test

cd sleep

# Full build (compile + tests + lint + coverage)
./gradlew build

# Unit tests only
./gradlew test

# Integration tests only (requires Docker)
./gradlew integrationTest

# Lint
./gradlew runKtlintCheck

# Coverage report (enforces 90% minimum)
./gradlew koverVerify

CI

GitHub Actions runs on every push to main and on pull requests (.github/workflows/build.yml). The pipeline executes ./gradlew build sonar, which compiles the project, runs unit tests, integration tests, enforces linting, and minimum code coverage, before publishing the results to SonarCloud. Branch protections on main require both a passing build and SonarCloud quality gate before merging.

CD / deployment is out of scope for this project.

Architecture

See docs/architecture.md for detailed architecture diagrams including:

Validate the Mermaid diagrams with:

npx -p @mermaid-js/mermaid-cli mmdc -i docs/architecture.md -o /tmp/validation-check.md

Design Decisions

Original Assignment

See ASSIGNMENT.md for the original assignment prompt.