Alma REST API Ruby library
This is a simple Ruby library that acts as a wrapper for the Ex Libris Alma REST APIs.
The main purpose of this library is to abstract authentication, error handling, and response parsing.
It uses faraday
as the underlying http client, nokogiri
for XML parsing, and oj
and hashie
for JSON processing.
Note: This is NOT an official Alma API client. It is developed at the University Library of Paderborn as an open source project and used in production as part of the library's discovery portal.
Installation
Add this to your Gemfile
:
gem "alma_api"
and run the bundle install
command in your terminal.
Usage
You need an API key for your Alma Instance in order to use this client. Please consult the Ex Libris developer documentation on how to use the Alma REST APIs for more information how to get and setup your API keys.
Note: There a some changes when upgrading from 1.x to 2.x. Please read the section on upgrading below.
Quick example
Here is a minimal example to get started. Read the sections below for more information on how to use this library.
# Create a client
client = AlmaApi::Client.configure do |config|
config.api_key = "YOUR API KEY"
end
# Use the client to get some users
users = client.get("users", params: {limit: 2})
Creating a configuration
To use this library you need an AlmaApi::Client
instance. To create a client you first need an AlmaApi::Configuration
.
# Normal method
configuration = AlmaApi::Configuration.new(api_key: "YOUR API KEY")
# ... or
configuration = AlmaApi::Configuration.new
configuration.api_key = "YOUR API KEY"
# Block-style
configuration = AlmaApi::Configuration.new do |config|
config.api_key = "YOUR API KEY"
end
The api_key
is the only required option to get a functional configuration. There are sensible default values for all other options.
The following options are available:
-
api_key
Set your Alma API key. This is the only option that can be passed to the constructor as a shortcut. All other options must be set using setters. -
base_url
Set the base URL to be used for each request. Ex Libris provides different API gateways for different geographical locations. See the documentation here for more information. This parameter is optional and defaults to the Alma API Gateway for Europe.This expects a
String
with a valid URL. However, you can use aSymbol
as a shortcut to set thebase_url
for one of the preconfigured gateways:na
(North America),:eu
(Europe),:ap
(Asia-Pacific),:ca
(Canada),:cn
(China).For example, to set the
base_url
for the canadian gateway, useconfiguration = AlmaApi::Configuration.new do |config| config.api_key = "YOUR API KEY" config.base_url = :ca )
-
default_format
The default format to use for each request. The client supports"json"
and"xml"
. The default is"json"
. -
language
The language used by Alma for error messages and textual information. The default is English ("en"
). To change this, set this parameter to any 2-letter language code that is supported and enabled in Alma (see the mapping table "Institution Languages" in Alma). -
timeout
The max number of seconds (Integer, Float) to wait for a request to complete. The default isnil
which uses the default of the underlying Faraday adapter (Net::HTTP
).
Creating a client
With the configuration ready, you can create the client.
client = AlmaApi::Client.new(configuration)
As a shortcut, you can call AlmaApi::Client.configure
with a block to get the client instance. Note that each call to AlmaApi::Client.configure
returns a new AlmaApi::Client
instance.
client = AlmaApi::Client.configure do |config|
config.api_key = "..."
...
end
Using the client
Calling Alma
The client provides the following methods: #get
, #post
, #put
and #delete
to call the Alma APIs with the corresponding HTTP methods GET
, POST
, PUT
and DELETE
.
Each method expects a URL path to the resource relative to the configured base_url
as it's first parameter. Parameters that the Alma API expects as part of the URL path must be included here.
To set query string parameters, set the params:
option and provide a Ruby Hash
. To override the default_format
for an individual request, you can set the format:
option to "json"
or "xml"
, depending on your needs. Setting the format to "xml"
is preferable for Alma APIs that work with MARCXML data.
To set the body of a #post
or #put
request, you can set the body:
option. If the request format is "json"
, the body:
option should contain a valid json string. Otherwise, if the request format is "xml"
, the option should be a valid XML string.
In the case of a JSON request, the result of the call is a Ruby Hash
. For a XML request, the result is a Nokogiri::XML::Document
instance, as this library uses nokogiri
under the hood for XML processing.
Get remaining API calls
Alma reports the number of remaining API calls as part of the response. You can call #remaining_api_calls
on the client to get this number. In case of an error this simply returns -1
.
Examples
GET
requests
Retrieve users
# Retrieve users (JSON)
users = client.get("users", params: {limit: 2})
# Retrieve users (XML)
users = client.get("users", params: {limit: 2}, format: :xml)
POST
and PUT
requests
Creating a user
# Prepare the data for a new user in Alma
user_data = {
record_type: {value: "PUBLIC"},
account_type: {value: "INTERNAL"},
preferred_language: {value: "de"},
status: {value: "ACTIVE"},
first_name: "FIRSTNAME",
last_name: "LASTNAME",
birth_date: "1978-07-07",
[...]
password: "SECRET PASSWORD",
force_password_change: true
}
# Create the user in Alma
user = client.post(
"users",
params: {
source_user_id: "xxx"
},
body: user_data.to_json
)
Updating a user
# First, get the user
user_id = "..." # a unique identifier for the user
user = client.get("users/#{user_id}") # user_id is a URL parameter
# Change the last name of the user
user["last_name"] = "..."
# Update the user in Alma
user = client.put("users/#{user_id}", body: user.to_json)
DELETE
requests
Deleting a user
user_id = "..." # a unique identifier for the user
client.delete("users/#{user_id}") # user_id is a URL parameter
Error handling
There are four types of errors that can occur when calling the Alma APIs with this library.
1. AlmaApi::Error
This is the base error class for this library. All errors raised within this library during a request will result in an AlmaApi::Error
or in one of the more specific sub classes listed below.
This error is also raised if something goes wrong when opening the connection, on SSL errors, network timeouts, etc.
The original error is wrapped and is available via the #cause
method.
Each error exposes #message
and #code
methods for further inspection.
Messages that are generated by Alma are returned in the language set in the configuration (default is English).
The code is generated by Alma. For gateway errors, the code is a string token (e.g. REQUEST_TOO_LARGE). For logical errors, the code is usually a number (e.g. 401850). See the "Possible Error Codes" section for each resource in the documentation for details.
For some errors, code may be nil
.
2. AlmaApi::GatewayError
If the Alma API responds with a 4xx
OR 5xx
HTTP status AND one of the following error codes, an AlmaApi::GatewayError
is raised.
GENERAL_ERROR
, UNAUTHORIZED
, INVALID_REQUEST
, PER_SECOND_THRESHOLD
, DAILY_THRESHOLD
, REQUEST_TOO_LARGE
, FORBIDDEN
, ROUTING_ERROR
Check the the documentation here for more information about gateway errors.
3. AlmaApi::ServerError
Any 5xx
HTTP status that does not result in an AlmaApi::GatewayError
will be raised as an AlmaApi::ServerError
.
4. AlmaApi::LogicalError
Any 4xx
HTTP status that does not result in an AlmaApi::GatewayError
will be raised as an AlmaApi::LogicalError
.
This is the most common error you will encounter and can be used to manage the control flow in your application.
For example, if you're loading a user's details, you usually don't want your application to blow up if a user with the specified user ID doesn't exist. Instead, you can handle the error like this:
def load_user(user_id)
client.get("users/#{user_id}")
rescue AlmaApi::LogicalError => e
# log the error
puts "Error #{e.code}: #{e.message}"
# ... the error code could be inspected and we could perform
# different things based on the error code but in this case
# we just return nil to indicate that the user does not exists.
nil
end
if (user = load_user("ALMA_USER_ID"))
puts "Hello #{user["first_name"]} #{user["last_name"]}"
else
puts "Too bad. No such user."
end
Tweaking a request
As stated before this library uses faraday
as the underlying http client. It manages the Faraday connection, sets up the necessary headers and params and performs the requests against Alma.
Therefore there should be no need to tweak a request before sending it to Alma. This should be considered as a bug and we are happy to receive feature requests.
However, there is still a way to tweak a request. For #get
, #post
, #put
and #delete
, you can open a block that gives you access to the Faraday::Request
instance, which you can use to manipulate the request before it is sent to Alma.
client.get("some/path") do |req|
req.headers["foo"] = "bar"
end
Upgrading
From 1.x to 2.x
-
AlmaApi.configure
is deprecated. UseAlmaApi::Client.configure
instead. - All errors that get raised during a request result in an
AlmaApi::Error
. Use#cause
to get the causing error. -
AlmaApi::Client#remaining_api_calls
performs a request to read the value from Alma. -
AlmaApi::Configuration
can be used to set a request timeout.