St. Baldrick's Ruby SDK

Installation

gem install stbaldricks

Configuration

  • The SDK is configured using environment variables.
  • Required configurations: API_ENDPOINT="https://stbaldricks.org/api" API_KEY="abc123"

Usage

  • Require the gem require 'stbaldricks'

  • Configure the log level (optional) SBF::Client::Configuration.logger.level = ::Logger::WARN

  • NOTE the SDK does not raise exceptions for failed requests. This is to allow for more complex logic around error scenarios.

    • If you would like the client to raise errors by default you can wrap your requests in a helper ``` ruby def request_helper # Yield to the block to execute the request client_response = yield

# If there was an error, raise it to the error handling block if client_response.error? puts "Error received from API. (code: #client_responseclient_response.http_code)" puts JSON.pretty_generate(client_response.to_hash)

raise "Error received from API"

end

# Otherwise simply return the client data client_response.data end


#### Default Entity Actions
##### Lookup
* Use `aggregate` to perform actions like gathering the size of a data set without returning all of the rows
``` ruby
active_donation_count = request_helper {
  SBF::Client::Donation.aggregate([status: :active], {id: :count})
}
puts "Beginning number of active donations: #{active_donation_count[:id]}"
  • Use get to get an entity by its id fields ruby existing_donation = request_helper { SBF::Client::Donation.get(188) } puts "Donation: #{JSON.pretty_generate(existing_donation.to_hash)}"

Creation

  • To create an entity, create the object using new and persist the object to the database using save ``` ruby # Find a random, active user to use as the recipient existing_participant = request_helper { SBF::Client::Participant.find(SBF::Client::Participant::Status::ACTIVE) }[:results].sample recipient = SBF::Client::Donation::PartialParticipant.new(id: existing_participant.id)

Create a new donor profile. This will be saved at the same time as the donation

donor = SBF::Client::Donation::FullPerson.new( status: SBF::Client::Person::Status::ACTIVE, gender: SBF::Client::Person::Gender::NOT_SELECTED, how_created: SBF::Client::Person::HowCreated::PAYMENT_SPRING_APP, name_pieces: SBF::Client::NamePieces.new( first_name: 'Foo', last_name: 'Testerman' ), email_addresses: { primary: SBF::Client::EmailAddress.new( type: SBF::Client::EmailAddress::Type::PERSONAL, email_address: '[email protected]' ) }, opt_out_settings: { email_mass_online: true } )

Payment details may be provided if the charging is completed.

Otherwise a nonce should be used which will be settled when the donation is saved

payment_details = SBF::Client::Payment::CreditCardDetails.new( authorization_id: 'abc123', cardholder_name: 'Foo Testerman', card_type: 'Visa', expiration_date: '02/2027', card_number: '************1234' )

Create the donation object itself. Status should be active unless this is a submit cash type donation.

donation = SBF::Client::FullDonation.new( status: SBF::Client::Donation::Status::ACTIVE, amount: 50.00, display_name: 'Test Display Name', is_unrecognized: false, how_created: SBF::Client::Donation::HowCreated::PAYMENT_SPRING_APP, donor: donor, recipient: recipient, payment_details: payment_details )

request_helper { donation.save } puts "Added donation #donationdonation.id"


#### Search Actions
##### Text search
``` ruby
# Find all participants or fundraisers with 'John' in the searchable fields
model_types = [SBF::Client::Search::Type::PARTICIPANT, SBF::Client::Search::Type::FUNDRAISER]
participants_and_fundraisers = request_helper { SBF::Client::Search.find(model_types, 'John')}[:results]
puts JSON.pretty_generate(participants_and_fundraisers)
# Find all active participants, teams, and fundraisers at the railyard event
railyard_event = request_helper { SBF::Client::Event.find(venue: {location: {name: {like: "%Rail%"}}}, year: 2017) }[:results].first

model_types = [SBF::Client::Search::Type::PARTICIPANT, SBF::Client::Search::Type::TEAM, SBF::Client::Search::Type::FUNDRAISER]
filter = {
  and: [
    {column: 'status_id', operator: 'equals', value: SBF::Client::Search::Status::ACTIVE},
    {column: 'event_id', operator: 'equals', value: railyard_event.id},
    {column: 'event_year', operator: 'equals', value: railyard_event.year}
  ]
}

entities_at_the_railyard = request_helper { SBF::Client::Search.find(model_types, nil, filter, nil, limit: 20) }[:results]
puts JSON.pretty_generate(entities_at_the_railyard)
# Find all active participants with 10 miles of Lincoln, NE
require 'geocoder'
data = Geocoder::Lookup.get(:google).search("Lincoln, NE").first
lat = data.latitude.round(6)
lon = data.longitude.round(6)
geo_location = {lat: lat, lon: lon, distance: 10}

model_type = SBF::Client::Search::Type::PARTICIPANT

filter = {and: [{column: 'status_id', operator: 'equals', value: SBF::Client::Search::Status::ACTIVE}]}

active_participant_within_10_mi = request_helper { SBF::Client::Search.find(model_type, nil, filter, geo_location, limit: 2) }[:results]
puts JSON.pretty_generate(active_participant_within_10_mi)

Architecture

Component Location
Endpoints /lib/stbaldricks/endpoints
Entities /lib/stbaldricks/entities
Enums /lib/stbaldricks/enums
Rspec Unit /spec/unit
Rspec Integration /spec/integration

Endpoints

  • Define supported requests that can be made to the SBF API for entities in the client library.
  • Extend SBF::Client::EntityEndpoint to receive common core action for the entities.
    • If additional or non-standard functionality is required, actions can be added or overridden.
  • Return a response object which will contain an http status code and depending on the code either error details or the expected API response data for a successful request.

Entities

  • Object representations of the data sent to and received from the API.
  • Top-level entities (those that can be directly requested or modified via the API) inherit from SBF::Client::TopLevelEntity.
    • Supported actions on the entity are defined via action and actions.
    • Any actions that are disallowed are blocked via blacklist_action.
    • The entities also map to their corresponding endpoint for handling all related API requests.
  • Sub-entities should still extend the SBF::Client::BaseEntity.

Enums

  • Collections of constants to assist with specifying values within a finite list.

Rspec Tests

Tests are broken out into unit and integration tests. Test coverage thresholds are enforced and test additions or modifications are required for nearly any client library change.

More detail on understanding and writing Rspec tests can be found in the Rspec Guide.

Running Tests

  • use rake client:ruby:test:all to run all tests
    • use TESTS={path-to-test} rake client:ruby:test:all to run specific test(s)