Ucb::Hcm

Ucb::Hcm is a lightweight ruby wrapper around UC Berkeley's Human Capital Management API.

The current version (3.x) is intended for v3 of the HCM API. If you need to access v2, you should use 1.0 of this gem.

Installation

Add this line to your application's Gemfile:

gem 'ucb-hcm', "~> 3"

And then execute:

$ bundle

Or install it by yourself:

$ gem install ucb-hcm

Configuration

Configure your app with your API credentials from UCB's API Central.

Ucb::Hcm.configure do |hcm|
  hcm.app_id  = "APP_ID"
  hcm.app_key = "APP_KEY"
  hcm.endpoint = "https://apis.berkeley.edu/uat/hr/v3"
end

Usage

:rotating_light: Note: Version 3.x of this gem only supports v3 of the API. :rotating_light:

See the API documentation for details on the different endpoints and what they return.

Note that the main part of the returned payload (["response"]) is always an Array. It will often have only one element, but there could be more, even if you're not expecting them. To make certain you're getting all the data you asked for, be sure to iterate over the payload.

Fetch Employees

    client = Ucb::Hcm::Client.new
    response = client.get("/employees", { limit: 20, previous: 0, next: 20 })

    >> response.all
    => [
          {
            "identifiers"=> [
                {"type"=>"hr-employee-id", "id"=>"10141478"}
            ]
          },
          {
            "identifiers"=>[
                {"type"=>"hr-employee-id", "id"=>"10272831"},
                {"type"=>"legacy-hr-employee-id", "id"=>"12521372"}
            ]
          }
          ...
        ]

Fetch specific Employee

    client = Ucb::Hcm::Client.new
    response = client.get("/employees/10272831", {"id-type" => "hr-employee-id"})

    >> response.all
    => [
         {
            "identifiers" => [
              {"type" => "campus-uid", "id" => "20108691"}, {"type" => "campus-solutions-id"}, {"type" => "student-id"}, {"type" => "hr-employee-id", "id" => "10272831"}, {"type" => "legacy-hr-employee-id"}, {"type" => "calnet-id"}
            ],
            "names" => [{
              "type "= >{"code" => "PRI", "description" => "Primary"},
              "familyName" => "Kumar",
              "givenName" => "Siri",
              "lastChangedBy" => {"id" => "10000499"},
              "fromDate" => "2018-10-01"
            }],
            ...
          }
        ]

Fetch an Employee's jobs

    client = Ucb::Hcm::Client.new
    response = client.get("/employees/10272831/jobs", {"id-type" => "hr-employee-id"})

    >> response.all
    => [
         {
            "identifiers" => [
              {"type" => "campus-uid", "id" => "20108691"}, {"type" => "campus-solutions-id"}, {"type" => "student-id"}, {"type" => "hr-employee-id", "id" => "10272831"}, {"type" => "legacy-hr-employee-id"}, {"type" => "calnet-id"}
            ],
            "jobs" => [
              {
                "number" => 0,
                "sequence" => 0,
                "type" => {
                  "code" => "2",
                  "description" => "Staff: Career"
                },
                ...
              }
              ...
            ]
        }
    ]

Parsing The Response

All of the API calls return an instance of Ucb::Hcm::Response. There are various ways to get to the underlying data, depending on your needs.

Checking The Response Status

You can find out if the call was successful by checking the return code, or just calling success?:

response = client.get("/employees/10272831/jobs", {"id-type" => "hr-employee-id"})
response.code
=> 200

response.success?
=> true

Any status code other than 200 will cause success? to return false

Extracting The Response Data: The Low-Level Approach

Calling raw_response will give the response object of the underlying HTTP library (currently HTTParty::Response). That object behaves like a hash, so you can access any part of the payload directly:

response = client.get("/employees/10272831/jobs", {"id-type" => "hr-employee-id"})
response.raw_response["source"]
=> "UCB-HR-PATH-DB"

The all method will return the contents of the response portion of the payload - this is where the meat of the data is usually found. The response node is always an Array of Hash objects even if one element is expected:

response = client.get("/employees/10272831/jobs", {"id-type" => "hr-employee-id"})

response.all
=> [
     {
        "identifiers" => [
          {"type" => "campus-uid", "id" => "20108691"}, {"type" => "campus-solutions-id"}, {"type" => "student-id"}, {"type" => "hr-employee-id", "id" => "10272831"}, {"type" => "legacy-hr-employee-id"}, {"type" => "calnet-id"}
        ],
        "jobs" => [
          {
            "number" => 0,
            "sequence" => 0,
            "type" => {
              "code" => "2",
              "description" => "Staff: Career"
            },
            ...
          }
          ...
        ]
    }
]

response.all.first["jobs"].first["number"]
=> 0

You can also iterate through each of the response items with the each method:

response = client.get("/employees/10272831/jobs", {"id-type" => "hr-employee-id"})

response.each do |item|
  puts item["jobs"].first["number"]
end
=> 0

Extracting The Response Data Using DataFetcher

The payloads from the HCM API are often very large and deeply nested, so navigating them as raw hashes can be a little cumbersome. To help get around this, it's possible to read the response data using the Ucb::Hcm::DataFetcher object.

DataFetcher Overview

A DataFetcher takes a hash when initialized and it can then be safely navigated using dot notation:

data = {foo: "bar", baz: { bam: "bang" }}
fetcher = Ucb::Hcm::DataFetcher.new(data)
fetcher.baz.bam.value
=> "bang"

You need to call value when you've reached the node you're trying to read.

If you hit a node that contains an array, you can use the usual Array methods to navigate through them:

data = {foo: "bar", baz: [{ bam: "bang" }]} # baz is an Array this time
fetcher = Ucb::Hcm::DataFetcher.new(data)
fetcher.baz.first.bam.value
=> "bang"

fetcher.baz[0].bam.value
=> "bang"

Apart from the conciseness of the syntax, DataFetcher has another advantage over digging through hashes, as it will handle nodes that are nil without raising an exception:

fetcher.baz.this_is_not_a_real_node.bam.value
=> nil

If any part of the traversal yields nil, the call to value will return nil but you can change this by passing a different default to the value call:

fetcher.baz.this_is_not_a_real_node.bam.value("oops!")
=> "oops!"

Getting DataFetchers From A Response

You can access an API payload with DataFetcher objects by calling all_fetchers or each_fetcher on a Response instance. These are equivalent to all and each (described earlier) but they return DataFetcher instances rather than hashes:

response = client.get("/employees/10272831/jobs", {"id-type" => "hr-employee-id"})

response.all.first.class
=> Hash

response.all_fetchers.first.class
=> Ucb::Hcm::DataFetcher

response.each do |item|
  p item.class
end
=> Hash

response.each_fetcher do |item|
  p item.class
end
=> Ucb::Hcm::DataFetcher

Contributing

  1. Fork it ( https://github.com/[my-github-username]/ucb-hcm/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Ask for .env file from previous developer and run source .env
  4. Commit your changes (git commit -am 'Add some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create a new Pull Request