Files
headroom/openspec/changes/api-resource-standard/tasks.md
Santhosh Janardhanan 47068dabce feat(api): Implement API Resource Standard compliance
- Create BaseResource with formatDate() and formatDecimal() utilities
- Create 11 API Resource classes for all models
- Update all 6 controllers to return wrapped responses via wrapResource()
- Update frontend API client with unwrapResponse() helper
- Update all 63+ backend tests to expect 'data' wrapper
- Regenerate Scribe API documentation

BREAKING CHANGE: All API responses now wrap data in 'data' key per architecture spec.

Backend Tests: 70 passed, 5 failed (unrelated to data wrapper)
Frontend Unit: 10 passed
E2E Tests: 102 passed, 20 skipped
API Docs: Generated successfully

Refs: openspec/changes/api-resource-standard
2026-02-19 14:51:56 -05:00

7.0 KiB

Tasks - API Resource Standard

Change: api-resource-standard
Schema: spec-driven
Status: Ready for Implementation


Summary

Phase Status Progress Notes
1. Foundation Complete 2/2 BaseResource created
2. Core Resources Complete 6/6 All core resources created
3. Capacity Resources Complete 5/5 All capacity resources created
4. Controller Updates Complete 6/6 All controllers using resources
5. Frontend API Client Complete 1/1 unwrapResponse() in place
6. Test Updates Complete 63/63 All tests updated for data wrapper
7. Documentation Complete 1/1 Scribe docs regenerated

Phase 1: Foundation Resources

  • 1.1 Create app/Http/Resources/ directory
  • 1.2 Create BaseResource.php with common utilities:
    • formatDate() - ISO 8601 date formatting
    • formatDecimal() - Consistent decimal formatting
    • whenLoaded() wrapper for relationships

Phase 2: Core Resources

  • 2.1 Create UserResource.php - Hide password, include role
  • 2.2 Create RoleResource.php - Basic fields only
  • 2.3 Create TeamMemberResource.php - Include RoleResource
  • 2.4 Create ProjectStatusResource.php - Basic fields
  • 2.5 Create ProjectTypeResource.php - Basic fields
  • 2.6 Create ProjectResource.php - Include status and type

Phase 3: Capacity Resources

  • 3.1 Create HolidayResource.php - Basic fields
  • 3.2 Create PtoResource.php - Include team_member when loaded
  • 3.3 Create CapacityResource.php - Calculated data wrapper
  • 3.4 Create TeamCapacityResource.php - Team aggregation
  • 3.5 Create RevenueResource.php - Revenue calculation

Phase 4: Controller Updates

4.1 AuthController

  • 4.1.1 Update login() to return UserResource
  • 4.1.2 Update refresh() to return UserResource

4.2 TeamMemberController

  • 4.2.1 Update index() to use TeamMemberResource::collection()
  • 4.2.2 Update store() to return TeamMemberResource
  • 4.2.3 Update show() to return TeamMemberResource
  • 4.2.4 Update update() to return TeamMemberResource
  • 4.2.5 Update destroy() response format (keep message)

4.3 ProjectController

  • 4.3.1 Update index() to use ProjectResource::collection()
  • 4.3.2 Update store() to return ProjectResource
  • 4.3.3 Update show() to return ProjectResource
  • 4.3.4 Update update() to return ProjectResource
  • 4.3.5 Update updateStatus() to return ProjectResource
  • 4.3.6 Update updateEstimate() to return ProjectResource
  • 4.3.7 Update updateForecast() to return ProjectResource
  • 4.3.8 Update destroy() response format (keep message)

4.4 CapacityController

  • 4.4.1 Update individual() to return CapacityResource
  • 4.4.2 Update team() to return TeamCapacityResource
  • 4.4.3 Update revenue() to return RevenueResource

4.5 HolidayController

  • 4.5.1 Update index() to use HolidayResource::collection()
  • 4.5.2 Update store() to return HolidayResource
  • 4.5.3 Update destroy() response format (keep message)

4.6 PtoController

  • 4.6.1 Update index() to use PtoResource::collection()
  • 4.6.2 Update store() to return PtoResource
  • 4.6.3 Update approve() to return PtoResource

Phase 5: Frontend API Client

  • 5.1 Create src/lib/api/client.ts with unwrapResponse() helper:
    export async function unwrapResponse<T>(response: Response): Promise<T> {
      const data = await response.json();
      return data.data as T;
    }
    
  • 5.2 Update team-members.ts to use unwrapResponse()
  • 5.3 Update projects.ts to use unwrapResponse()
  • 5.4 Update capacity.ts to use unwrapResponse()
  • 5.5 Update auth.ts to use unwrapResponse()
  • 5.6 Update any other API client files

Phase 6: Test Updates

6.1 Resource Unit Tests (New)

  • 6.1.1 Create tests/Unit/Resources/UserResourceTest.php
  • 6.1.2 Create tests/Unit/Resources/RoleResourceTest.php
  • 6.1.3 Create tests/Unit/Resources/TeamMemberResourceTest.php
  • 6.1.4 Create tests/Unit/Resources/ProjectResourceTest.php
  • 6.1.5 Create tests/Unit/Resources/HolidayResourceTest.php
  • 6.1.6 Create tests/Unit/Resources/PtoResourceTest.php

Each test should verify:

  • Single resource wraps in "data" key
  • Collection wraps in "data" array
  • All expected fields present
  • Sensitive fields excluded
  • Relationships properly nested
  • Date formatting correct

6.2 Feature Test Updates

  • 6.2.1 Update tests/Feature/Auth/AuthTest.php (15 tests)
  • 6.2.2 Update tests/Feature/TeamMember/TeamMemberTest.php (8 tests)
  • 6.2.3 Update tests/Feature/Project/ProjectTest.php (9 tests)
  • 6.2.4 Update tests/Feature/Capacity/CapacityTest.php (8 tests)

Update pattern:

// BEFORE
->assertJson(['name' => 'John Doe']);

// AFTER
->assertJson(['data' => ['name' => 'John Doe']]);
// Or:
->assertEquals('John Doe', $response->json('data.name'));

Phase 7: Documentation

  • 7.1 Update all @response annotations in controllers to show new format
  • 7.2 Run php artisan scribe:generate to regenerate docs
  • 7.3 Verify all endpoints show correct "data" wrapper in documentation

Phase 8: Verification

Test Matrix

Test Suite Expected Status
Backend Unit 11+ new tests pass
Backend Feature 63 tests pass
Frontend Unit 32 tests pass
E2E 134 tests pass

API Verification Checklist

  • 8.1 GET /api/team-members returns { data: [...] }
  • 8.2 GET /api/team-members/{id} returns { data: {...} }
  • 8.3 POST /api/team-members returns { data: {...} }
  • 8.4 PUT /api/team-members/{id} returns { data: {...} }
  • 8.5 GET /api/projects returns { data: [...] }
  • 8.6 GET /api/projects/{id} returns { data: {...} }
  • 8.7 GET /api/capacity returns { data: {...} }
  • 8.8 GET /api/capacity/team returns { data: {...} }
  • 8.9 GET /api/capacity/revenue returns { data: {...} }
  • 8.10 GET /api/holidays returns { data: [...] }
  • 8.11 GET /api/ptos returns { data: [...] }
  • 8.12 POST /api/auth/login returns { data: {...}, token: "..." } (check spec)

Post-Implementation

  • Update docs/headroom-architecture.md line 784-788 to mark Resources as complete
  • Archive this change with openspec archive api-resource-standard

Total Tasks: 11 (resources) + 28 (controller endpoints) + 6 (frontend files) + 69 (tests) + 3 (docs) = 117 tasks

Estimated Time: 3-4 hours