alma_api 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2251cc05e84f6e62b33f7138a6a29016a1d78821
4
- data.tar.gz: 8df5fa4c4ae44cf958b45b0f86d42aa306a2f10f
2
+ SHA256:
3
+ metadata.gz: cbcf2ac797d9d76c1d536a4eba0d084033fe3993f98f55dd02120114ad9df5eb
4
+ data.tar.gz: cf70240bb22810542d8ed2400e98eb656412cb1339eecb1605badd9a7019603c
5
5
  SHA512:
6
- metadata.gz: ffe7b8a6ec6d0256686540beda16362ffd32fcb7bffb40eb3028c1b4f04788ac711e269ca25fee41f4e64ce6cc8ecfdb47850bc2b4252447a0bea5c0a834e0ec
7
- data.tar.gz: de96d0f55d7de94861ecc0c0305b9e2e23ba2b7f3515ce0e7311558a2a06c50bd2184fd1471f5d302aa441d867cde7ddc5e4a9d513e553158bd14e2fb8af5e71
6
+ metadata.gz: 266823640ec75ba8962f163a80737ba9ec1f19cf70f4286ec88de278ea7ee9c4efdf9da5a0dad664ab3f918b82d690d41a9a67ccb4f4b8949db3ceb770accb6e
7
+ data.tar.gz: 7fdd946b9040bea59f14ce74ad2457736290b9c31baa31b3b1ed443a5391dc2bff96a66fe0d0e23b208b03597c08eca236c778cb25349a15a60a0af0c31d9586
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Universitätsbibliothek Paderborn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,62 +1,181 @@
1
- # alma_api
1
+ # Alma REST API Ruby library ![Tests](https://github.com/ubpb/alma_api/actions/workflows/tests.yml/badge.svg)
2
2
 
3
- This is a ruby wrapper for the Alma REST api. It aims to make working with this api as pleasant as possible. Options and parameters are the same as for the original api. It also sanitizes some api quirks such as unnecessary case sensitive option values.
3
+ This is a simple Ruby library that acts as a wrapper for the
4
+ [Ex Libris Alma REST APIs](https://developers.exlibrisgroup.com/alma/apis/).
5
+
6
+ The main purpose of this library is to abstract authentication, error handling, and response parsing.
7
+
8
+ It uses [`faraday`](https://github.com/lostisland/faraday) as the underlying http client, [`nokogiri`](https://github.com/sparklemotion/nokogiri) for XML parsing, and [`oj`](https://github.com/ohler55/oj) and [`hashie`](https://github.com/hashie/hashie) for JSON processing.
9
+
10
+ __Note: This is NOT an official Alma API client. It is developed at the University Library of Paderborn as an open source project.__
4
11
 
5
12
  ## Installation
6
13
 
7
- Add this line to your application's Gemfile:
14
+ Add this to your `Gemfile`:
8
15
 
9
16
  ```ruby
10
17
  gem "alma_api"
11
18
  ```
19
+ and run the `bundle install` command in your terminal.
12
20
 
13
- And then execute:
21
+ ## Usage
14
22
 
15
- $ bundle
23
+ > 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](https://developers.exlibrisgroup.com/alma/apis/#using) for more information how to get and setup your API keys.
16
24
 
17
- Or install it yourself as:
25
+ ### Creating a configuration
18
26
 
19
- $ gem install alma_api
27
+ To use this library you need an `AlmaApi::Client` instance. The client requires an `AlmaApi::Configuration`.
20
28
 
21
- ## Usage
29
+ ```ruby
30
+ configuration = AlmaApi::Configuration.new(
31
+ api_key: "...", # 1. required
32
+ base_url: "...", # 2. optional
33
+ default_format: "...", # 3. optional
34
+ language: "..." # 4. optional
35
+ )
36
+ ```
37
+
38
+ 1. `api_key` Add your Alma API key here.
39
+ 2. `base_url` Add the base URL to be used for each request. Ex Libris provides different API gateways for different geographical locations. See [the documentation here](https://developers.exlibrisgroup.com/alma/apis/#calling) for more information. This parameter is optional and defaults to the Alma API Gateway for Europe: `https://api-eu.hosted.exlibrisgroup.com/almaws/v1`.
40
+ 3. `default_format` The default format to use for each request. The client supports `json` and `xml`. The default is `json`.
41
+ 4. `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).
42
+
43
+ ### Creating a client
22
44
 
23
- In order to use the api client you have to provide an apikey and the host you want to connect to. Both can be provided as paramters or as environment variables named ```ALMA_APIKEY``` or ```ALMA_HOST```.
45
+ With the configuration ready, you can create the client.
24
46
 
25
47
  ```ruby
26
- require "alma_api"
48
+ client = AlmaApi::Client.new(configuration)
49
+ ```
50
+
51
+ As a shortcut, you can call `AlmaApi.configure` to get the client instance. Note that each call to `AlmaApi.configure` returns a new `AlmaApi::Client` instance.
52
+
53
+ ```ruby
54
+ client = AlmaApi.configure do |config|
55
+ api_key: "...",
56
+ base_url: "...",
57
+ default_format: "...",
58
+ language: "..."
59
+ end
60
+ ```
61
+ ### Using the client
62
+
63
+ 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`.
64
+
65
+ 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.
66
+
67
+ 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.
68
+
69
+ 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.
70
+
71
+ In the case of a `json` request, the result of the call is a Ruby `hash`. For `xml`, the result is a `Nokogiri::XML::Document` instance, as this library uses [`nokogiri`](https://github.com/sparklemotion/nokogiri) under the hood for XML processing.
72
+
73
+ ## Examples
74
+
75
+ #### `GET` requests
27
76
 
28
- client = AlmaApi::Client.new(apikey: "your_api_key", host: "https://alma_host")
77
+ __Retrieve users__
78
+ ```ruby
79
+ # Retrieve users (JSON)
80
+ users = client.get("users", params: {limit: 2})
29
81
 
30
- # calling semantic resembles the api as close as possible
31
- client.users.get # => [...] users collection
32
- client.users("user_id").get # => user object
33
- client.users("user_id").get(view: :brief) # => user object (brief view)
34
- client.users("user_id").fees.get # => fees of a user
35
- client.users("user_id").fees("fee_id").get # => fees of a user
82
+ # Retrieve users (XML)
83
+ users = client.get("users", params: {limit: 2}, format: :xml)
36
84
  ```
37
85
 
38
- This gem uses ```mighty_struct``` for its results. This means you can navigate results with object notation like with ```OpenStruct```. If you want to get the underlaying hash/array just call `to_a` or `to_h`.
86
+ #### `POST` and `PUT` requests
87
+
88
+ __Creating a user__
89
+ ```ruby
90
+ # Prepare the data for a new user in Alma
91
+ user_data = {
92
+ record_type: {value: "PUBLIC"},
93
+ account_type: {value: "INTERNAL"},
94
+ preferred_language: {value: "de"},
95
+ status: {value: "ACTIVE"},
96
+ first_name: "FIRSTNAME",
97
+ last_name: "LASTNAME",
98
+ birth_date: "1978-07-07",
99
+ [...]
100
+ password: "SECRET PASSWORD",
101
+ force_password_change: true
102
+ }
103
+
104
+ # Create the user in Alma
105
+ user = client.post(
106
+ "users",
107
+ params: {
108
+ source_user_id: "xxx"
109
+ },
110
+ body: user_data.to_json
111
+ )
112
+ ```
39
113
 
114
+ __Updating a user__
40
115
  ```ruby
41
- require "alma_api"
116
+ # First, get the user
117
+ user_id = "..." # a unique identifier for the user
118
+ user = client.get("users/#{user_id}") # user_id is a URL parameter
119
+
120
+ # Change the last name of the user
121
+ user["last_name"] = "..."
42
122
 
43
- client = AlmaApi::Client.new(apikey: "your_api_key", host: "https://alma_host")
44
- some_user = client.users(client.users.get.first.primary_id).get
123
+ # Update the user in Alma
124
+ user = client.put("users/#{user_id}", body: user.to_json)
45
125
 
46
- # now you can investigate the user with method notation
47
- some_user.contact_info.email.first.email_address # => "some@email_adress.org"
48
126
  ```
49
127
 
50
- ## Development
128
+ #### `DELETE` requests
129
+
130
+ __Deleting a user__
131
+ ```ruby
132
+ user_id = "..." # a unique identifier for the user
133
+ client.delete("users/#{user_id}") # user_id is a URL parameter
134
+ ```
135
+
136
+ ## Error handling
137
+
138
+ There are three types of errors that can occur when calling the Alma APIs with this library. Each error exposes the `#message` and `#code` methods for further inspection. The message is returned in the language set in the configuration (default is English).
139
+
140
+ 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](https://developers.exlibrisgroup.com/alma/apis/) for details.
51
141
 
52
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
142
+ ### 1. `AlmaApi::GatewayError`
53
143
 
54
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
144
+ If the Alma API responds with a `4xx` OR `5xx` HTTP status AND one of the following error codes, an `AlmaApi::GatewayError` is thrown.
55
145
 
56
- ## Contributing
146
+ `GENERAL_ERROR`, `UNAUTHORIZED`, `INVALID_REQUEST`, `PER_SECOND_THRESHOLD`, `DAILY_THRESHOLD`, `REQUEST_TOO_LARGE`, `FORBIDDEN`, `ROUTING_ERROR`
57
147
 
58
- 1. Fork it ( https://github.com/ubpb/alma_api/fork )
59
- 2. Create your feature branch (`git checkout -b my-new-feature`)
60
- 3. Commit your changes (`git commit -am 'Add some feature'`)
61
- 4. Push to the branch (`git push origin my-new-feature`)
62
- 5. Create a new Pull Request
148
+ Check the [the documentation here](https://developers.exlibrisgroup.com/alma/apis/#error) for more information about gateway errors.
149
+
150
+ ### 2. `AlmaApi::ServerError`
151
+
152
+ Any `5xx` HTTP status that does not result in an `AlmaApi::GatewayError` will be thrown as an `AlmaApi::ServerError`.
153
+
154
+ ### 3. `AlmaApi::LogicalError`
155
+
156
+ Any `4xx` HTTP status that does not result in an `AlmaApi::GatewayError` will be thrown as an `AlmaApi::LogicalError`.
157
+
158
+ This is the most common error you will encounter and can be used to manage the control flow in your application.
159
+
160
+ For example, if you're loading a user's details, you 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:
161
+
162
+ ```ruby
163
+ def load_user(user_id)
164
+ client.get("users/#{user_id}")
165
+ rescue AlmaApi::LogicalError => e
166
+ # log the error
167
+ puts "Error #{e.code}: #{e.message}"
168
+
169
+ # ... the error code could be inspected and we could perform
170
+ # different things based on the error code but in this case
171
+ # we just return nil to indicate that the user does not exists.
172
+ nil
173
+ end
174
+
175
+ if (user = load_user("ALMA_USER_ID"))
176
+ puts "Hello #{user["first_name"]} #{user["last_name"]}"
177
+ else
178
+ puts "Too bad. No such user."
179
+ end
180
+
181
+ ```
data/alma_api.gemspec CHANGED
@@ -1,27 +1,22 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path("lib", __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
4
  require "alma_api/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "alma_api"
8
8
  spec.version = AlmaApi::VERSION
9
- spec.authors = ["Michael Sievers"]
10
- spec.summary = %q{A ruby wrapper for the Alma REST api}
11
- spec.homepage = "https://github.com/ubpb/alma-api"
9
+ spec.authors = ["René Sprotte"]
10
+ spec.summary = "A Ruby client library for the Ex Libris Alma REST APIs"
11
+ spec.homepage = "http://github.com/ubpb/alma_api"
12
+ spec.license = "MIT"
12
13
 
13
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
14
- spec.bindir = "exe"
15
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
14
+ spec.files = `git ls-files lib README.md LICENSE alma_api.gemspec`.split($INPUT_RECORD_SEPARATOR)
16
15
  spec.require_paths = ["lib"]
17
16
 
18
- spec.add_dependency "activesupport"
19
- spec.add_dependency "faraday"
20
- spec.add_dependency "mighty_struct", ">= 0.1.2"
21
-
22
- spec.add_development_dependency "benchmark-ips"
23
- spec.add_development_dependency "bundler", ">= 1.3"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec", ">= 3.0.0", "< 4.0.0"
26
- spec.add_development_dependency "simplecov", ">= 0.8.0"
17
+ spec.add_dependency "activesupport", "> 6", "< 8"
18
+ spec.add_dependency "faraday", "~> 2.7"
19
+ spec.add_dependency "hashie", "~> 5.0"
20
+ spec.add_dependency "nokogiri", "~> 1.11"
21
+ spec.add_dependency "oj", "~> 3.11"
27
22
  end
@@ -1,35 +1,144 @@
1
- require "faraday"
2
- require_relative "../alma_api"
1
+ module AlmaApi
2
+ class Client
3
3
 
4
- class AlmaApi::Client
5
- require_relative "./client/users"
4
+ attr_reader :configuration,
5
+ :remaining_api_calls
6
6
 
7
- API_ROOT_PATH="/almaws"
7
+ def initialize(configuration)
8
+ @configuration = configuration
9
+ @remaining_api_calls = -1
10
+ end
8
11
 
9
- attr_accessor :apikey
10
- attr_accessor :host
12
+ def get(url, params: {}, format: nil)
13
+ perform_request do
14
+ connection(format: format, params: params).get(url)
15
+ end
16
+ end
11
17
 
12
- alias_method :api_key, :apikey
13
- alias_method :api_key=, :apikey=
18
+ def post(url, params: {}, body: nil, format: nil)
19
+ perform_request do
20
+ connection(format: format, params: params).post(url) do |req|
21
+ req.body = body
22
+ end
23
+ end
24
+ end
14
25
 
15
- def initialize(options = {})
16
- options.symbolize_keys.try do |_sanitized_options|
17
- @apikey = _sanitized_options[:apikey] || _sanitized_options[:api_key] || ENV["ALMA_APIKEY"] || ENV["ALMA_API_KEY"]
18
- @host = (_sanitized_options[:host] || ENV["ALMA_HOST"]).try(:strip)
26
+ def put(url, params: {}, body: nil, format: nil)
27
+ perform_request do
28
+ connection(format: format, params: params).put(url) do |req|
29
+ req.body = body
30
+ end
31
+ end
19
32
  end
20
- end
21
33
 
22
- def users(user_id = nil)
23
- Users.new(self, user_id)
24
- end
34
+ def delete(url, params: {}, format: nil)
35
+ perform_request do
36
+ connection(format: format, params: params).delete(url)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def connection(format: nil, params: {})
43
+ format = case AlmaApi.validate_format!(format)
44
+ when "xml" then "application/xml"
45
+ when "json" then "application/json"
46
+ else
47
+ "application/json"
48
+ end
49
+
50
+ default_params = {
51
+ lang: configuration.language
52
+ }.reject do |k, v|
53
+ k == :lang && (v.blank? || v == "en")
54
+ end
55
+
56
+ Faraday.new(
57
+ configuration.base_url,
58
+ params: default_params.reverse_merge(params),
59
+ headers: {
60
+ "Authorization": "apikey #{configuration.api_key}",
61
+ "Accept": format,
62
+ "Content-Type": format
63
+ }
64
+ ) do |faraday|
65
+ faraday.response :raise_error # raise Faraday::Error on status code 4xx or 5xx
66
+ end
67
+ end
68
+
69
+ def perform_request
70
+ response = yield
71
+ set_remaining_api_calls(response)
72
+ parse_response_body(response.body)
73
+ rescue Faraday::Error => e
74
+ error = parse_error_response_body(e.response[:body])
75
+
76
+ case error[:error_code]
77
+ when *GATEWAY_ERROR_CODES
78
+ raise GatewayError.new(error[:error_message], error[:error_code])
79
+ else
80
+ case e.response[:status]
81
+ when 400..499
82
+ raise LogicalError.new(error[:error_message], error[:error_code])
83
+ when 500..599
84
+ raise ServerError.new(error[:error_message], error[:error_code])
85
+ else # this should not happen
86
+ # :nocov:
87
+ raise ServerError.new(error[:error_message], error[:error_code])
88
+ # :nocov:
89
+ end
90
+ end
91
+ end
92
+
93
+ def set_remaining_api_calls(response)
94
+ rac = response.headers[:x_alma_api_remaining]
95
+ @remaining_api_calls = rac.to_i if rac.present?
96
+ end
97
+
98
+ def parse_response_body(body)
99
+ if body.blank?
100
+ nil
101
+ elsif is_xml_response?(body)
102
+ Nokogiri::XML.parse(body)
103
+ elsif is_json_response?(body)
104
+ Oj.load(body)
105
+ else
106
+ raise Error.new("Unsupported content type in response from API.", "API_CLIENT_ERROR")
107
+ end
108
+ end
109
+
110
+ def parse_error_response_body(body)
111
+ if is_xml_response?(body)
112
+ xml = Nokogiri::XML.parse(body)
113
+ error_message = xml.at("errorMessage")&.text
114
+ error_code = xml.at("errorCode")&.text
115
+
116
+ {error_message: error_message, error_code: error_code}
117
+ elsif is_json_response?(body)
118
+ json = Oj.load(body)
119
+ json.extend Hashie::Extensions::DeepFind
120
+
121
+ # Sometimes the format is:
122
+ # {"errorList":{"error":[{"errorCode":"xxx","errorMessage":"xxx"}]}}
123
+ # and sometimes the format is:
124
+ # {"web_service_result":{"errorList":{"error":{"errorMessage":"xxx","errorCode":"xxx"}}}}
125
+ # so we use a deep find to find the first occurrence of "errorMessage" and "errorCode"
126
+ error_message = json.deep_find("errorMessage")
127
+ error_code = json.deep_find("errorCode")
128
+
129
+ {error_message: error_message, error_code: error_code}
130
+ else
131
+ {error_message: nil, error_code: nil}
132
+ end
133
+ end
134
+
135
+ def is_xml_response?(body)
136
+ body&.strip&.starts_with?("<")
137
+ end
25
138
 
26
- # inter-client api
27
- def http(method, path, options = {})
28
- connection = Faraday.new.tap do |_connection|
29
- _connection.headers["Accept"] = "application/json"
30
- _connection.params["apikey"] = apikey
139
+ def is_json_response?(body)
140
+ body&.strip&.starts_with?("{")
31
141
  end
32
142
 
33
- connection.send(method, "#{host}#{API_ROOT_PATH}#{path}", options)
34
143
  end
35
144
  end
@@ -0,0 +1,34 @@
1
+ module AlmaApi
2
+ class Configuration
3
+
4
+ attr_reader :api_key,
5
+ :base_url,
6
+ :default_format,
7
+ :language
8
+
9
+ def initialize(api_key: nil, base_url: nil, default_format: nil, language: nil)
10
+ self.api_key = api_key
11
+ self.base_url = base_url
12
+ self.default_format = default_format
13
+ self.language = language
14
+ end
15
+
16
+ def api_key=(value)
17
+ @api_key = value.presence
18
+ end
19
+
20
+ def base_url=(value)
21
+ base_url = value.presence || "https://api-eu.hosted.exlibrisgroup.com/almaws/v1"
22
+ @base_url = base_url&.ends_with?("/") ? base_url[0..-2] : base_url
23
+ end
24
+
25
+ def default_format=(value)
26
+ @default_format = AlmaApi.validate_format!(value) || "json"
27
+ end
28
+
29
+ def language=(value)
30
+ @language = value.presence || "en"
31
+ end
32
+
33
+ end
34
+ end
@@ -1,3 +1,3 @@
1
1
  module AlmaApi
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0".freeze
3
3
  end
data/lib/alma_api.rb CHANGED
@@ -1,11 +1,57 @@
1
1
  require "active_support"
2
2
  require "active_support/core_ext"
3
- require "alma_api/version"
3
+ require "faraday"
4
+ require "nokogiri"
5
+ require "hashie"
6
+ require "oj"
7
+
8
+ require "alma_api/configuration"
9
+ require "alma_api/client"
4
10
 
5
11
  module AlmaApi
6
- require_relative "./alma_api/client"
12
+ DEFAULT_ERROR_CODE = "UNKNOWN".freeze
13
+ DEFAULT_ERROR_MESSAGE = "Unknown cause".freeze
14
+
15
+ class Error < StandardError
16
+ attr_reader :code
17
+
18
+ def initialize(message, code)
19
+ @code = code.presence || DEFAULT_ERROR_CODE
20
+ super(message.presence || DEFAULT_ERROR_MESSAGE)
21
+ end
22
+ end
23
+
24
+ class GatewayError < Error; end
25
+ class ServerError < Error; end
26
+ class LogicalError < Error; end
27
+
28
+ GATEWAY_ERROR_CODES = [
29
+ "GENERAL_ERROR",
30
+ "UNAUTHORIZED",
31
+ "INVALID_REQUEST",
32
+ "PER_SECOND_THRESHOLD",
33
+ "DAILY_THRESHOLD",
34
+ "REQUEST_TOO_LARGE",
35
+ "FORBIDDEN",
36
+ "ROUTING_ERROR"
37
+ ].freeze
38
+
39
+ class << self
40
+
41
+ def configure
42
+ configuration = Configuration.new
43
+ yield(configuration) if block_given?
44
+ Client.new(configuration)
45
+ end
46
+
47
+ def validate_format!(format)
48
+ case format = format&.to_s
49
+ when "json", "xml" then format
50
+ when nil then nil
51
+ else
52
+ raise ArgumentError, "Unsupported format '#{format}'. Only 'json' and 'xml' is supported."
53
+ end
54
+ end
7
55
 
8
- def self.debug(arg)
9
- binding.pry
10
56
  end
11
57
  end
metadata CHANGED
@@ -1,162 +1,109 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alma_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Michael Sievers
8
- autorequire:
9
- bindir: exe
7
+ - René Sprotte
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-20 00:00:00.000000000 Z
11
+ date: 2023-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - ">"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '6'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - ">="
27
+ - - ">"
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: '6'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: faraday
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - ">="
37
+ - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '0'
39
+ version: '2.7'
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
- - - ">="
44
+ - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: '0'
46
+ version: '2.7'
41
47
  - !ruby/object:Gem::Dependency
42
- name: mighty_struct
48
+ name: hashie
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: 0.1.2
53
+ version: '5.0'
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: 0.1.2
55
- - !ruby/object:Gem::Dependency
56
- name: benchmark-ips
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: bundler
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '1.3'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
58
+ - - "~>"
81
59
  - !ruby/object:Gem::Version
82
- version: '1.3'
60
+ version: '5.0'
83
61
  - !ruby/object:Gem::Dependency
84
- name: rake
62
+ name: nokogiri
85
63
  requirement: !ruby/object:Gem::Requirement
86
64
  requirements:
87
- - - ">="
65
+ - - "~>"
88
66
  - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: rspec
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: 3.0.0
104
- - - "<"
105
- - !ruby/object:Gem::Version
106
- version: 4.0.0
107
- type: :development
67
+ version: '1.11'
68
+ type: :runtime
108
69
  prerelease: false
109
70
  version_requirements: !ruby/object:Gem::Requirement
110
71
  requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: 3.0.0
114
- - - "<"
72
+ - - "~>"
115
73
  - !ruby/object:Gem::Version
116
- version: 4.0.0
74
+ version: '1.11'
117
75
  - !ruby/object:Gem::Dependency
118
- name: simplecov
76
+ name: oj
119
77
  requirement: !ruby/object:Gem::Requirement
120
78
  requirements:
121
- - - ">="
79
+ - - "~>"
122
80
  - !ruby/object:Gem::Version
123
- version: 0.8.0
124
- type: :development
81
+ version: '3.11'
82
+ type: :runtime
125
83
  prerelease: false
126
84
  version_requirements: !ruby/object:Gem::Requirement
127
85
  requirements:
128
- - - ">="
86
+ - - "~>"
129
87
  - !ruby/object:Gem::Version
130
- version: 0.8.0
131
- description:
132
- email:
88
+ version: '3.11'
89
+ description:
90
+ email:
133
91
  executables: []
134
92
  extensions: []
135
93
  extra_rdoc_files: []
136
94
  files:
137
- - ".gitignore"
138
- - ".rspec"
139
- - ".travis.yml"
140
- - Gemfile
141
- - LICENSE.txt
95
+ - LICENSE
142
96
  - README.md
143
- - Rakefile
144
97
  - alma_api.gemspec
145
- - bin/console
146
- - bin/setup
147
98
  - lib/alma_api.rb
148
99
  - lib/alma_api/client.rb
149
- - lib/alma_api/client/users.rb
150
- - lib/alma_api/client/users/fees.rb
151
- - lib/alma_api/client/users/loans.rb
152
- - lib/alma_api/client/users/requests.rb
153
- - lib/alma_api/collection.rb
154
- - lib/alma_api/object.rb
100
+ - lib/alma_api/configuration.rb
155
101
  - lib/alma_api/version.rb
156
- homepage: https://github.com/ubpb/alma-api
157
- licenses: []
102
+ homepage: http://github.com/ubpb/alma_api
103
+ licenses:
104
+ - MIT
158
105
  metadata: {}
159
- post_install_message:
106
+ post_install_message:
160
107
  rdoc_options: []
161
108
  require_paths:
162
109
  - lib
@@ -171,9 +118,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
118
  - !ruby/object:Gem::Version
172
119
  version: '0'
173
120
  requirements: []
174
- rubyforge_project:
175
- rubygems_version: 2.4.6
176
- signing_key:
121
+ rubygems_version: 3.4.10
122
+ signing_key:
177
123
  specification_version: 4
178
- summary: A ruby wrapper for the Alma REST api
124
+ summary: A Ruby client library for the Ex Libris Alma REST APIs
179
125
  test_files: []
data/.gitignore DELETED
@@ -1,9 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --color
2
- --format documentation
3
- --require spec_helper
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - "2.0"
4
- - "2.1"
5
- - "2.2"
data/Gemfile DELETED
@@ -1,19 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in your gemspec
4
- gemspec
5
-
6
- if !ENV["CI"]
7
- group :development do
8
- gem "hashdiff"
9
- gem "pry", "~> 0.9.12.6"
10
- gem "pry-byebug", "<= 1.3.2"
11
- gem "pry-rescue", "~> 1.4.1", github: "ConradIrwin/pry-rescue", branch: :master
12
- gem "pry-stack_explorer", "~> 0.4.9.1"
13
- gem "pry-syntax-hacks", "~> 0.0.6"
14
- end
15
- end
16
-
17
- group :test do
18
- gem "codeclimate-test-reporter", require: nil
19
- end
data/LICENSE.txt DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2015 Michael Sievers
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile DELETED
@@ -1,14 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
7
-
8
- task :benchmark do
9
- require_relative "./benchmark/joffrey/mab_document"
10
- require_relative "./benchmark/joffrey/mab_xml_parser"
11
-
12
- #Benchmark::Joffrey::MabDocument.new.call
13
- Benchmark::Joffrey::MabXmlParser.new.call
14
- end
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "alma_api"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- require "pry"
11
- Pry.start
12
-
13
- #require "irb"
14
- #IRB.start
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here
@@ -1,40 +0,0 @@
1
- require "alma_api/collection"
2
- require "json"
3
- require_relative "../users"
4
-
5
- class AlmaApi::Client::Users::Fees
6
- def initialize(client, user_id, fee_id = nil)
7
- @client = client
8
- @fee_id = fee_id
9
- @user_id = user_id
10
- end
11
-
12
- def get(options = {})
13
- if @fee_id
14
- (options = options.deep_dup).symbolize_keys!.reverse_merge!({
15
- user_id_type: :all_unique
16
- })
17
-
18
- @client.http(:get, "/v1/users/#{@user_id}/fees/#{@fee_id}", options).try do |_response|
19
- if _response.status == 200
20
- AlmaApi::Object.new(JSON.parse(_response.body))
21
- end
22
- end
23
-
24
- else
25
- (options = options.deep_dup).symbolize_keys!.reverse_merge!({
26
- user_id_type: :all_unique,
27
- status: :ACTIVE
28
- })
29
-
30
- # sanitize api quirks
31
- options[:status] = options[:status].to_s.upcase.to_sym
32
-
33
- @client.http(:get, "/v1/users/#{@user_id}/fees", options).try do |_response|
34
- if _response.status == 200
35
- AlmaApi::Collection.new(JSON.parse(_response.body), "fee")
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,30 +0,0 @@
1
- require "alma_api/collection"
2
- require "json"
3
- require_relative "../users"
4
-
5
- class AlmaApi::Client::Users::Loans
6
- def initialize(client, user_id, loan_id = nil)
7
- @client = client
8
- @loan_id = loan_id
9
- @user_id = user_id
10
- end
11
-
12
- def get(options = {})
13
- (options = options.deep_dup).symbolize_keys!.reverse_merge!({
14
- user_id_type: :all_unique,
15
- limit: 10,
16
- offset: 0,
17
- order_by: :id,
18
- direction: :ASC
19
- })
20
-
21
- # sanitize api quirks
22
- options[:direction] = options[:direction].to_s.upcase.to_sym
23
-
24
- @client.http(:get, "/v1/users/#{@user_id}/loans", options).try do |_response|
25
- if _response.status == 200
26
- AlmaApi::Collection.new(JSON.parse(_response.body), "item_loan")
27
- end
28
- end
29
- end
30
- end
@@ -1,30 +0,0 @@
1
- require "alma_api/collection"
2
- require "json"
3
- require_relative "../users"
4
-
5
- class AlmaApi::Client::Users::Requests
6
- def initialize(client, user_id, request_id = nil)
7
- @client = client
8
- @request_id = request_id
9
- @user_id = user_id
10
- end
11
-
12
- def get(options = {})
13
- (options = options.deep_dup).symbolize_keys!.reverse_merge!({
14
- request_type: nil, # :HOLD, :DIGITIZATION, :BOOKING
15
- limit: 10,
16
- offset: 0,
17
- status: :active # :active, :history
18
- })
19
-
20
- # sanitize api quirks
21
- options[:request_type] = options[:request_type].try(:to_s).try(:upcase).try(:to_sym)
22
- options[:status] = options[:status].to_s.downcase.to_sym
23
-
24
- @client.http(:get, "/v1/users/#{@user_id}/requests", options).try do |_response|
25
- if _response.status == 200
26
- AlmaApi::Collection.new(JSON.parse(_response.body), "user_request")
27
- end
28
- end
29
- end
30
- end
@@ -1,58 +0,0 @@
1
- require "alma_api/collection"
2
- require "alma_api/object"
3
- require "json"
4
- require_relative "../client"
5
-
6
- class AlmaApi::Client::Users
7
- require_relative "./users/fees"
8
- require_relative "./users/loans"
9
- require_relative "./users/requests"
10
-
11
- def initialize(client, user_id = nil)
12
- @client = client
13
- @user_id = user_id
14
- end
15
-
16
- def get(options = {})
17
- if @user_id
18
- (options = options.deep_dup).symbolize_keys!.reverse_merge!({
19
- user_id_type: :all_unique,
20
- view: :full
21
- })
22
-
23
- @client.http(:get, "/v1/users/#{@user_id}", options).try do |_response|
24
- if _response.status == 200
25
- AlmaApi::Object.new(JSON.parse(_response.body))
26
- end
27
- end
28
- else
29
- (options = options.deep_dup).symbolize_keys!.reverse_merge!({
30
- limit: 10,
31
- offset: 0,
32
- order_by: [
33
- :last_name,
34
- :first_name,
35
- :primary_id
36
- ]
37
- })
38
-
39
- @client.http(:get, "/v1/users", options).try do |_response|
40
- if _response.status == 200
41
- AlmaApi::Collection.new(JSON.parse(_response.body), "user")
42
- end
43
- end
44
- end
45
- end
46
-
47
- def fees(fee_id = nil)
48
- Fees.new(@client, @user_id, fee_id)
49
- end
50
-
51
- def loans(loan_id = nil)
52
- Loans.new(@client, @user_id, loan_id)
53
- end
54
-
55
- def requests(request_id = nil)
56
- Requests.new(@client, @user_id, request_id)
57
- end
58
- end
@@ -1,28 +0,0 @@
1
- require_relative "./object"
2
- require_relative "../alma_api"
3
-
4
- class AlmaApi::Collection < Delegator
5
- def initialize(hash, items_key, item_class = AlmaApi::Object)
6
- hash.each do |_key, _value|
7
- if _key != items_key
8
- singleton_class.class_eval do
9
- attr_accessor "#{_key}".to_sym
10
- end
11
-
12
- instance_variable_set("@#{_key}".to_sym, _value)
13
- end
14
- end
15
-
16
- @items = hash.try(:[], items_key).try(:map) do |_item|
17
- item_class.new(_item)
18
- end || []
19
- end
20
-
21
- def __getobj__
22
- @items
23
- end
24
-
25
- def __setobj__(obj)
26
- @items = obj
27
- end
28
- end
@@ -1,16 +0,0 @@
1
- require "mighty_struct"
2
- require_relative "../alma_api"
3
-
4
- class AlmaApi::Object < MightyStruct
5
- def initialize(object)
6
- if object.is_a?(Hash)
7
- object.each do |_key, _value|
8
- if _value.is_a?(String) && _value[/\A\d\d\d\d-\d\d-\d\d\w.*/]
9
- object[_key] = Date.parse(_value)
10
- end
11
- end
12
- end
13
-
14
- super(object)
15
- end
16
- end