nypl_ruby_util 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a102fc9301f667018c0ae2c9f2fc26c5525f8ac2fad91b35b980da52731b67b2
4
+ data.tar.gz: 4a3ac1f699931ca2432196a3c4997948ded44e6e470ec3bd958cb639b0731eca
5
+ SHA512:
6
+ metadata.gz: 9cb6c355ffc6d948bd800e7e6b69267115f67f49cb644aa14212d296556a0e95acdd2d8004e88f31f9671e38d4eca6a8ce349a7a18bc52fe694c990e6f6a561c
7
+ data.tar.gz: 59e711f7bebcacfe8dc24520ec8c2bcde349d2bffabc1ad40386622cf55096bdde87ee7aff4f088f70408d76466e67d1a0be634f7d96457ba5265b3304b15e70
data/lib/directory.rb ADDED
@@ -0,0 +1,3 @@
1
+ class Directory
2
+ # PLATFORM_API_BASE_URL
3
+ end
data/lib/errors.rb ADDED
@@ -0,0 +1,10 @@
1
+ class NYPLError < StandardError
2
+ attr_reader :object
3
+
4
+ def initialize(object='')
5
+ @object = object
6
+ end
7
+ end
8
+
9
+ class AvroError < NYPLError
10
+ end
@@ -0,0 +1,49 @@
1
+ require 'securerandom'
2
+ require 'aws-sdk-kinesis'
3
+ require_relative 'nypl_avro'
4
+ require_relative 'errors'
5
+ # Model representing the result message posted to Kinesis stream about everything that has gone on here -- good, bad, or otherwise.
6
+
7
+ class KinesisClient
8
+ attr_reader :config, :avro
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ @avro = nil
13
+
14
+ if config[:schema_string]
15
+ @avro = NYPLAvro.by_name(config[:schema_string])
16
+ end
17
+
18
+ end
19
+
20
+ def <<(json_message)
21
+ p '<< ', config[:schema_string], avro
22
+ if config[:schema_string]
23
+ message = avro.encode(json_message)
24
+ else
25
+ message = json_message
26
+ end
27
+
28
+ client = Aws::Kinesis::Client.new
29
+ partition_key = (config[:partition_key] ? json_message[config[:partition_key]] : SecureRandom.hex(20)).hash.to_s
30
+
31
+ resp = client.put_record({
32
+ stream_name: config[:stream_name],
33
+ data: message,
34
+ partition_key: partition_key
35
+ })
36
+
37
+ return_hash = {}
38
+
39
+ if resp.successful?
40
+ return_hash["code"] = "200"
41
+ return_hash["message"] = json_message, resp
42
+ $logger.info("Message sent to HoldRequestResult #{json_message}, #{resp}") if $logger
43
+ else
44
+ $logger.error("message" => "FAILED to send message to HoldRequestResult #{json_message}, #{resp}.") if $logger
45
+ raise NYPLError.new json_message, resp
46
+ end
47
+ return_hash
48
+ end
49
+ end
data/lib/kms_client.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'aws-sdk-kms'
2
+ require 'base64'
3
+
4
+ class KmsClient
5
+ @@kms = nil
6
+
7
+ def initialize(options = {})
8
+ options ||= {}
9
+ @kms = self.class.aws_kms_client(options)
10
+ end
11
+
12
+ def decrypt(cipher)
13
+ # Assume value is base64 encoded:
14
+ decoded = Base64.decode64 cipher
15
+ decrypted = @kms.decrypt ciphertext_blob: decoded
16
+ decrypted[:plaintext]
17
+ end
18
+
19
+ def self.aws_kms_client(options)
20
+ params = {
21
+ region: 'us-east-1',
22
+ stub_responses: ENV['APP_ENV'] == 'test'
23
+ }.merge(options)
24
+ @@kms = Aws::KMS::Client.new(params) if @@kms.nil?
25
+ @@kms
26
+ end
27
+ end
data/lib/nypl_avro.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+ require 'avro'
3
+ require 'base64'
4
+
5
+ require_relative 'errors'
6
+ require_relative 'directory'
7
+
8
+ class NYPLAvro
9
+ def initialize (schemaString)
10
+ begin
11
+ @schema = Avro::Schema.parse(schemaString)
12
+ rescue Exception => e
13
+ raise AvroError.new(e), "Failed to parse schema string: \"#{schemaString}\""
14
+ end
15
+
16
+ @reader = Avro::IO::DatumReader.new(@schema)
17
+ end
18
+
19
+ def decode(encoded_data_string, base64 = true)
20
+ avro_string = base64 ? Base64.decode64(encoded_data_string) : encoded_data_string
21
+ stringreader = StringIO.new(avro_string)
22
+ bin_decoder = Avro::IO::BinaryDecoder.new(stringreader)
23
+ begin
24
+ read_value = @reader.read(bin_decoder)
25
+ rescue Exception => e
26
+ raise AvroError.new(e), "Error decoding data using #{@schema.name} schema"
27
+ end
28
+
29
+ read_value
30
+ end
31
+
32
+ def encode(decoded_data, base64 = true)
33
+ bin_encoder = Avro::IO::DatumWriter.new(@schema)
34
+ buffer = StringIO.new
35
+ encoder = Avro::IO::BinaryEncoder.new(buffer)
36
+
37
+ begin
38
+ bin_encoder.write(decoded_data, encoder)
39
+ rescue Avro::IO::AvroTypeError => e
40
+ raise AvroError.new(e), "Error encoding data using #{@schema.name} schema due to #{e.message}"
41
+ end
42
+
43
+ buffer.rewind
44
+ result = buffer.read
45
+ base64 ? Base64.encode64(result) : result
46
+ end
47
+
48
+ def self.by_name (name)
49
+ require 'net/http'
50
+ require 'uri'
51
+
52
+ uri = URI.parse("#{ENV["PLATFORM_API_BASE_URL"]}current-schemas/#{name}")
53
+ begin
54
+ response = Net::HTTP.get_response(uri)
55
+ rescue Exception => e
56
+ raise AvroError.new(e), "Failed to retrieve #{name} schema: #{e.message}"
57
+ end
58
+
59
+ begin
60
+ response_hash = JSON.parse(response.body)
61
+ rescue JSON::ParserError => e
62
+ raise AvroError.new(e), "Retrieved #{name} schema is malformed: #{response.body}"
63
+ end
64
+
65
+ raise AvroError.new, "Failed to retrieve #{name} schema: statusCode=#{response_hash["statusCode"]}" if response_hash["statusCode"] >= 400
66
+ raise AvroError.new, "Retrieved #{name} schema is malformed" if response_hash["data"].nil? || response_hash["data"]["schema"].nil?
67
+
68
+ self.new response_hash["data"]["schema"]
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ require 'nypl_log_formatter'
2
+ require 'nypl_sierra_api_client'
3
+ require_relative 'kms_client'
4
+ require_relative 'platform_api_client'
5
+ require_relative 'directory'
6
+ require_relative 'nypl_avro'
7
+ require_relative 'errors'
8
+ require_relative 'kinesis_client'
9
+
10
+ class NYPLRubyUtil
11
+ class SierraApiClient < SierraApiClient
12
+ end
13
+
14
+ class NyplLogFormatter < NyplLogFormatter
15
+ end
16
+
17
+ class KmsClient < KmsClient
18
+ end
19
+
20
+ class NYPLAvro < NYPLAvro
21
+ end
22
+
23
+ class PlatformApiClient < PlatformApiClient
24
+ end
25
+
26
+ class KinesisClient < KinesisClient
27
+ end
28
+ end
@@ -0,0 +1,105 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+
5
+ require_relative 'kms_client'
6
+
7
+ class PlatformApiClient
8
+ attr_reader :authenticated, :client_id, :client_secret, :error_options, :oauth_site
9
+ attr_accessor :access_token
10
+
11
+ def initialize(options = {})
12
+ raise 'Missing config: NYPL_OAUTH_ID is unset' if ENV['NYPL_OAUTH_ID'].nil? || ENV['NYPL_OAUTH_ID'].empty?
13
+ raise 'Missing config: NYPL_OAUTH_SECRET is unset' if ENV['NYPL_OAUTH_SECRET'].nil? || ENV['NYPL_OAUTH_SECRET'].empty?
14
+
15
+ kms_client = KmsClient.new(options[:kms_options])
16
+ @client_id = kms_client.decrypt(ENV['NYPL_OAUTH_ID'])
17
+ @client_secret = kms_client.decrypt(ENV['NYPL_OAUTH_SECRET'])
18
+
19
+ @oauth_site = ENV['NYPL_OAUTH_URL']
20
+ @authenticated = options[:authenticated] || true
21
+ @error_options = default_errors.merge(options[:errors] || {})
22
+ end
23
+
24
+ def get (path)
25
+
26
+ authenticate! if authenticated
27
+
28
+ uri = URI.parse("#{ENV['PLATFORM_API_BASE_URL']}#{path}")
29
+
30
+ $logger.debug "Getting from platform api", { uri: uri }
31
+
32
+ begin
33
+ request = Net::HTTP::Get.new(uri)
34
+ request["Authorization"] = "Bearer #{access_token}" if authenticated
35
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme === 'https') do |http|
36
+ http.request(request)
37
+ end
38
+
39
+ $logger.debug "Got platform api response", { code: response.code, body: response.body }
40
+
41
+ parse_json_response response, path
42
+
43
+ rescue Exception => e
44
+ raise StandardError.new(e), "Failed to retrieve #{path} #{e.message}"
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def parse_json_response (response, path)
51
+ code = response.code.to_i
52
+ if code < 400
53
+ JSON.parse(response.body)
54
+ elsif error_options[code]
55
+ instance_exec(response, path, &error_options[code])
56
+ else
57
+ raise "Error interpretting response for path #{path}: (#{response.code}): #{response.body}"
58
+ {}
59
+ end
60
+ end
61
+
62
+ # Authorizes the request.
63
+ def authenticate!
64
+ # NOOP if we've already authenticated
65
+ return nil if ! access_token.nil?
66
+
67
+ uri = URI.parse("#{oauth_site}oauth/token")
68
+ request = Net::HTTP::Post.new(uri)
69
+ request.basic_auth(client_id, client_secret)
70
+ request.set_form_data(
71
+ "grant_type" => "client_credentials"
72
+ )
73
+
74
+ req_options = {
75
+ use_ssl: uri.scheme == "https",
76
+ request_timeout: 500
77
+ }
78
+
79
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
80
+ http.request(request)
81
+ end
82
+
83
+ if response.code == '200'
84
+ access_token = JSON.parse(response.body)["access_token"]
85
+ else
86
+ nil
87
+ end
88
+ end
89
+
90
+ def default_errors
91
+ {
92
+ 401 => lambda do |response, path|
93
+ if @try_count < 1
94
+ # Likely an expired access-token; Wipe it for next run
95
+ @try_count += 1
96
+ access_token = nil
97
+ get(path)
98
+ else
99
+ raise "Error interpretting response for path #{path}: (#{response.code}): #{response.body}"
100
+ end
101
+ end
102
+ }
103
+ end
104
+
105
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nypl_ruby_util
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Appel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A repository of common utilities for NYPL Ruby application
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/directory.rb
20
+ - lib/errors.rb
21
+ - lib/kinesis_client.rb
22
+ - lib/kms_client.rb
23
+ - lib/nypl_avro.rb
24
+ - lib/nypl_ruby_util.rb
25
+ - lib/platform_api_client.rb
26
+ homepage:
27
+ licenses:
28
+ - MIT
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubygems_version: 3.0.3
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: A repository of common utilities for NYPL Ruby application
49
+ test_files: []