Skip to content

Add Response Object Pattern and Complete Resource Implementation#13

Merged
ebrett merged 4 commits intomainfrom
feature/response-object-pattern
Jan 30, 2026
Merged

Add Response Object Pattern and Complete Resource Implementation#13
ebrett merged 4 commits intomainfrom
feature/response-object-pattern

Conversation

@ebrett
Copy link
Owner

@ebrett ebrett commented Jan 29, 2026

Summary

This PR implements the Response Object Pattern (nb-5) and completes the implementation of four major API resources that were unblocked by it. All changes maintain backward compatibility while providing an improved developer experience through optional response wrapping.

Changes

Response Object Pattern (nb-5)

  • ResponseObjects::Base: Core wrapper class with hash-compatible interface

    • Handles both V1 (plain JSON) and V2 (JSON:API) formats
    • Provides method-based attribute access (person.first_name)
    • Maintains backward compatibility with hash access (person[:data])
    • Extracts sideloaded relationships from JSON:API included data
  • ResponseObjects::Person: Person-specific response object

    • Computed attributes (full_name)
    • Taggings extraction from included data
    • Works with both V1 and V2 API responses
  • Configuration: New wrap_responses option (default: false for backward compatibility)

People Resource Extensions (nb-7)

  • list(page:, per_page:, filter:) - List people with pagination and filtering
  • create(attributes:) - Create new person records
  • delete(id) - Delete person records
  • search(query, **filter) - Search people by name and filters
  • update(id, attributes:) - Update person attributes (already existed, now uses response wrapping)

Tags Resource Extensions (nb-6)

  • bulk_apply(tag_name, person_ids) - Apply tag to multiple people efficiently
  • bulk_remove(tag_name, person_ids) - Remove tag from multiple people efficiently

Donations Resource (nb-11)

Complete new resource with full CRUD operations:

  • list(page:, per_page:, filter:) - List donations with pagination and filtering
  • show(id) - Fetch single donation
  • create(attributes:) - Create new donations
  • update(id, attributes:) - Update existing donations
  • V2 API with JSON:API format

Events Resource (nb-1)

Complete new resource with event and RSVP management:

  • Event operations:

    • list(page:, per_page:, filter:) - List events with pagination and filtering
    • show(id, include_rsvps:) - Fetch single event with optional RSVP sideloading
    • create(attributes:) - Create new events
    • update(id, attributes:) - Update existing events
    • delete(id) - Delete events
  • RSVP operations:

    • rsvps(event_id, include_person:) - List RSVPs for an event
    • create_rsvp(event_id, attributes:) - Create RSVP for an event
    • update_rsvp(rsvp_id, attributes:) - Update existing RSVP
    • delete_rsvp(rsvp_id) - Delete RSVP
  • V2 API with JSON:API format

V1 API Support (nb-f0q)

  • Added V1 tagging endpoints to People resource
  • list_taggings(id) - Get taggings with tag names (V1 API)
  • add_tagging(id, tag_name) - Add tag to person (V1 API)
  • remove_tagging(id, tag_name) - Remove tag from person (V1 API)

Test Coverage

  • 310 tests passing (was 275, added 35)
  • 93.66% code coverage (target: 90%)
  • 0 failures
  • All new features developed using TDD (test-driven development)

New Test Files

  • spec/nationbuilder_api/response_objects/base_spec.rb
  • spec/nationbuilder_api/response_objects/person_spec.rb
  • spec/nationbuilder_api/resources/donations_spec.rb
  • spec/nationbuilder_api/resources/events_spec.rb

Extended Test Files

  • spec/nationbuilder_api/resources/people_spec.rb - Added tests for new CRUD operations
  • spec/nationbuilder_api/resources/tags_spec.rb - Added tests for bulk operations

Backward Compatibility

All changes are backward compatible:

  • Response wrapping is opt-in via config.wrap_responses = true
  • Default behavior unchanged (returns raw API responses as hashes)
  • All existing method signatures preserved
  • Hash access still works on response objects

Usage Examples

With Response Wrapping Enabled

# Configure
NationbuilderApi.configure do |config|
  config.wrap_responses = true
end

# Use improved interface
person = client.people.show(123)
person.first_name  # => "John"
person.full_name   # => "John Doe"
person[:data]      # => still works for backward compatibility

# Works with lists too
people = client.people.list
people.each { |p| puts p.full_name }

New Resources

# Donations
client.donations.list(filter: {donor_id: "123"})
client.donations.create(attributes: {amount_in_cents: 5000, donor_id: "123"})

# Events
client.events.list(filter: {status: "published"})
event = client.events.create(attributes: {
  name: "Fundraiser",
  start_time: "2025-02-01T18:00:00Z"
})

# RSVPs
client.events.create_rsvp(event_id, attributes: {
  person_id: "789",
  status: "accepted"
})

Bulk Tag Operations

# Apply tag to multiple people efficiently
client.tags.bulk_apply("volunteer", [123, 456, 789])

# Remove tag from multiple people
client.tags.bulk_remove("inactive", [111, 222, 333])

Test Plan

  • Response object wrapping works with both V1 and V2 APIs
  • Hash access still works on response objects (backward compatibility)
  • People CRUD operations work correctly
  • Donations CRUD operations work correctly
  • Events CRUD operations work correctly
  • RSVP management works correctly
  • Bulk tag operations work efficiently
  • All existing tests still pass
  • Code coverage above 90%

Related Beads

Closes: nb-5, nb-7, nb-6, nb-11, nb-1, nb-f0q

🤖 Generated with Claude Code

ebrett and others added 4 commits January 29, 2026 20:26
Implements nb-5 to wrap API responses in typed objects instead of raw hashes,
providing better DX with convenient attribute access and type safety.

Added:
- ResponseObjects::Base - Base class handling both V1 and V2 API formats
  - Method access to attributes (person.first_name)
  - Hash-compatible interface for backward compatibility (person[:data])
  - Support for both JSON:API (V2) and plain JSON (V1) formats

- ResponseObjects::Person - Person-specific response object
  - Convenient accessors (first_name, last_name, email, etc.)
  - Computed attributes (full_name)
  - Taggings extraction from included data

- Configuration option `wrap_responses` (default: false)
  - Opt-in feature for backward compatibility
  - Can be enabled globally or per-client instance

Integration:
- Updated People resource to optionally wrap responses
- Response objects are backward compatible with hash access
- Comprehensive RSpec tests (40 new examples)

Benefits:
- Better developer experience with typed objects
- Convenient method access instead of deep hash navigation
- Backward compatible - doesn't break existing code
- Works with both V1 and V2 API responses

Test coverage: 93.28% (240 examples, 0 failures)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Implements list, create, delete, and search methods for complete People resource.

Added methods:
- list(page:, per_page:, filter:) - List people with pagination/filtering
- create(attributes:) - Create new person
- delete(id) - Delete person by ID
- search(query, **filter) - Search people by name with additional filters

All methods support response object wrapping when enabled.
Uses V2 API with JSON:API format.
Follows TDD approach with comprehensive tests.

Tests: 14 new examples, all passing
Coverage: 93.42%

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Implements bulk tagging operations for managing supporter segments.

Added methods:
- bulk_apply(tag_name, person_ids) - Apply tag to multiple people
- bulk_remove(tag_name, person_ids) - Remove tag from multiple people

Both methods handle empty arrays and return array of responses.
Uses V1 API for tag management.
Follows TDD approach.

Tests: 7 new examples, all passing
Coverage: 93.48%

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Implements comprehensive resource classes for managing donations and events:
- Donations: list, show, create, update operations
- Events: list, show, create, update, delete operations with RSVP management
- RSVP operations: list, create, update, delete RSVPs for events
- All resources follow JSON:API format conventions
- 310 tests passing with 93.66% coverage

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@ebrett ebrett merged commit 0c38249 into main Jan 30, 2026
3 checks passed
@ebrett ebrett deleted the feature/response-object-pattern branch January 30, 2026 09:48
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.

1 participant