nypl_sierra_api_client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8936adc2234429844a356e11687cb660933b0ad20f22acac447a09f9595af0d0
4
+ data.tar.gz: e14ab0690f2d3b8b9a340a1248a4746a9c9c418298c8028e9be096d380a204c5
5
+ SHA512:
6
+ metadata.gz: a50a652ee9278599ca077c5fa3a197128163e0f91eb8d2bd57db3152a31bdea413004541abf49d060eb907fae2a3fd8edf0dfcf0fd0d60b893b1beb69a4600df
7
+ data.tar.gz: 7dd271f819f77d1f58cdb4aa0b12c61921cb41ec7cf29eeaf6d637444e251328a89136479f7a9f888f9d92b5758de77c8e63517db78db86671ce67371301edd1
data/lib/errors.rb ADDED
@@ -0,0 +1,13 @@
1
+ class SierraApiClientError < StandardError
2
+ end
3
+
4
+ class SierraApiClientTokenError < SierraApiClientError
5
+ end
6
+
7
+ class SierraApiResponseError < StandardError
8
+ attr_reader :response
9
+
10
+ def initialize(response)
11
+ @response = response
12
+ end
13
+ end
@@ -0,0 +1,139 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'nypl_log_formatter'
5
+
6
+ require_relative 'errors'
7
+ require_relative 'sierra_api_response'
8
+
9
+ class SierraApiClient
10
+ def initialize(config = {})
11
+ @config = {
12
+ base_url: ENV['SIERRA_API_BASE_URL'],
13
+ client_id: ENV['SIERRA_OAUTH_ID'],
14
+ client_secret: ENV['SIERRA_OAUTH_SECRET'],
15
+ oauth_url: ENV['SIERRA_OAUTH_URL'],
16
+ log_level: 'info'
17
+ }.merge config
18
+
19
+ raise SierraApiClientError.new 'Missing config: neither config.base_url nor ENV.SIERRA_API_BASE_URL are set' unless @config[:base_url]
20
+ raise SierraApiClientError.new 'Missing config: neither config.client_id nor ENV.SIERRA_OAUTH_ID are set' unless @config[:client_id]
21
+ raise SierraApiClientError.new 'Missing config: neither config.client_secret nor ENV.SIERRA_OAUTH_SECRET are set ' unless @config[:client_secret]
22
+ raise SierraApiClientError.new 'Missing config: neither config.oauth_url nor ENV.SIERRA_OAUTH_URL are set ' unless @config[:oauth_url]
23
+ end
24
+
25
+ def get (path, options = {})
26
+ options = parse_http_options options
27
+
28
+ do_request 'get', path, options
29
+ end
30
+
31
+ def post (path, body, options = {})
32
+ options = parse_http_options options
33
+
34
+ # Default to POSTing JSON unless explicitly stated otherwise
35
+ options[:headers]['Content-Type'] = 'application/json' unless options[:headers]['Content-Type']
36
+
37
+ do_request 'post', path, options do |request|
38
+ request.body = body
39
+ request.body = request.body.to_json unless options[:headers]['Content-Type'] != 'application/json'
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def do_request (method, path, options = {})
46
+ # For now, these are the methods we support:
47
+ raise SierraApiClientError, "Unsupported method: #{method}" unless ['get', 'post'].include? method.downcase
48
+
49
+ authenticate! if options[:authenticated]
50
+
51
+ uri = URI.parse("#{@config[:base_url]}#{path}")
52
+
53
+ http = Net::HTTP.new(uri.host, uri.port)
54
+ http.use_ssl = uri.scheme === 'https'
55
+
56
+ # Build request headers:
57
+ request_headers = {}
58
+ request_headers['Content-Type'] = options[:headers]['Content-Type'] unless options.dig(:headers, 'Content-Type').nil?
59
+
60
+ # Create HTTP::Get or HTTP::Post
61
+ request = Net::HTTP.const_get(method.capitalize).new(uri.path, request_headers)
62
+
63
+ # Add bearer token header
64
+ request['Authorization'] = "Bearer #{@access_token}" if options[:authenticated]
65
+
66
+ # Allow caller to modify the request before we send it off:
67
+ yield request if block_given?
68
+
69
+ logger.debug "SierraApiClient: #{method} to Sierra api", { uri: uri, body: request.body }
70
+
71
+ begin
72
+ # Execute request:
73
+ response = http.request(request)
74
+ rescue => e
75
+ raise SierraApiClientError.new(e), "Failed to #{method} to #{path}: #{e.message}"
76
+ end
77
+
78
+ logger.debug "SierraApiClient: Got Sierra api response", { code: response.code, body: response.body }
79
+
80
+ parse_response response
81
+ end
82
+
83
+ def parse_response (response)
84
+ if response.code == "401"
85
+ # Likely an expired access-token; Wipe it for next run
86
+ # TODO: Implement token refresh
87
+ @access_token = nil
88
+ raise SierraApiClientTokenError.new("Got a 401: #{response.body}")
89
+ end
90
+
91
+ SierraApiResponse.new(response)
92
+ end
93
+
94
+ def parse_http_options (_options)
95
+ options = {
96
+ authenticated: true
97
+ }.merge _options
98
+
99
+ options[:headers] = {
100
+ }.merge(_options[:headers] || {})
101
+ .transform_keys(&:to_s)
102
+
103
+ options
104
+ end
105
+
106
+ # Authorizes the request.
107
+ def authenticate!
108
+ # NOOP if we've already authenticated
109
+ return nil if ! @access_token.nil?
110
+
111
+ logger.debug "SierraApiClient: Authenticating with client_id #{@config[:client_id]}"
112
+
113
+ uri = URI.parse("#{@config[:oauth_url]}")
114
+ request = Net::HTTP::Post.new(uri)
115
+ request.basic_auth(@config[:client_id], @config[:client_secret])
116
+ request.set_form_data(
117
+ "grant_type" => "client_credentials"
118
+ )
119
+
120
+ req_options = {
121
+ use_ssl: uri.scheme == "https",
122
+ request_timeout: 500
123
+ }
124
+
125
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
126
+ http.request(request)
127
+ end
128
+
129
+ if response.code == '200'
130
+ @access_token = JSON.parse(response.body)["access_token"]
131
+ else
132
+ nil
133
+ end
134
+ end
135
+
136
+ def logger
137
+ @logger ||= NyplLogFormatter.new(STDOUT, level: @config[:log_level])
138
+ end
139
+ end
@@ -0,0 +1,34 @@
1
+ class SierraApiResponse
2
+ attr_accessor :response
3
+
4
+ def initialize(response)
5
+ @response = response
6
+ end
7
+
8
+ def code
9
+ @response.code.to_i
10
+ end
11
+
12
+ def error?
13
+ code >= 400
14
+ end
15
+
16
+ def success?
17
+ (200...300).include? code
18
+ end
19
+
20
+ def body
21
+ return @response.body if @response.code == '204'
22
+
23
+ # If response Content-Type indicates body is json
24
+ if /^application\/json/ =~ @response['Content-Type']
25
+ begin
26
+ JSON.parse(@response.body)
27
+ rescue => e
28
+ raise SierraApiResponseError.new(response), "Error parsing response (#{response.code}): #{response.body}"
29
+ end
30
+ else
31
+ @response.body
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nypl_sierra_api_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - nonword
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-04-21 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Client for querying Sierra API
14
+ email: paulbeaudoin@nypl.org
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/errors.rb
20
+ - lib/sierra_api_client.rb
21
+ - lib/sierra_api_response.rb
22
+ homepage: https://github.com/NYPL/ruby-sierra-api-client
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.0.8
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: NYPL Sierra API client
45
+ test_files: []