reso_transport 1.5.2 → 1.5.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,10 +18,11 @@ module ResoTransport
18
18
  authorize_request(request_env)
19
19
 
20
20
  @app.call(request_env).on_complete do |response_env|
21
- raise_if_unauthorized(response_env)
21
+ raise_if_unauthorized(request_env, response_env)
22
22
  end
23
23
  rescue ResoTransport::AccessDenied
24
- raise if retries == 0
24
+ raise if retries.zero?
25
+
25
26
  @auth.reset
26
27
  retries -= 1
27
28
  retry
@@ -38,8 +39,8 @@ module ResoTransport
38
39
  )
39
40
  end
40
41
 
41
- def raise_if_unauthorized(response_env)
42
- raise ResoTransport::AccessDenied if response_env[:status] == 401
42
+ def raise_if_unauthorized(request_env, response_env)
43
+ raise ResoTransport::AccessDenied.new(request_env.to_h, response_env) if response_env[:status] == 401
43
44
  end
44
45
  end
45
46
  end
@@ -0,0 +1,64 @@
1
+ module ResoTransport
2
+ class BaseMetadata
3
+ MIME_TYPES = {
4
+ xml: 'application/xml',
5
+ json: 'application/json'
6
+ }.freeze
7
+
8
+ attr_reader :client
9
+
10
+ def initialize(client)
11
+ @client = client
12
+ @prefix = nil
13
+ @classname = nil
14
+ end
15
+
16
+ def prefix
17
+ raise 'prefix not set' unless @prefix
18
+
19
+ @prefix
20
+ end
21
+
22
+ def classname
23
+ raise 'classname not set' unless @classname
24
+
25
+ @classname
26
+ end
27
+
28
+ def parser
29
+ @parser ||= Object::const_get("#{classname}Parser").new.parse(data)
30
+ end
31
+
32
+ def data
33
+ if cache_file
34
+ cache.read || cache.write(raw)
35
+ else
36
+ raw
37
+ end
38
+ end
39
+
40
+ def cache
41
+ @cache ||= client.send("#{prefix}_cache").new(cache_file)
42
+ end
43
+
44
+ def cache_file
45
+ @cache_file ||= client.send "#{prefix}_file"
46
+ end
47
+
48
+ def raw
49
+ return response.body if response.success?
50
+
51
+ raise RequestError.new(request, response, classname)
52
+ end
53
+
54
+ def response
55
+ raise 'Must implement response method'
56
+ end
57
+
58
+ def request
59
+ return @request.to_h if @request.respond_to? :to_h
60
+
61
+ {}
62
+ end
63
+ end
64
+ end
@@ -1,34 +1,52 @@
1
1
  module ResoTransport
2
2
  class Client
3
- attr_reader :connection, :uid, :vendor, :endpoint, :auth, :md_file
4
-
3
+ attr_reader :connection, :uid, :vendor, :endpoint, :authentication, :md_file, :md_cache, :ds_file, :ds_cache,
4
+ :use_replication_endpoint
5
+
5
6
  def initialize(options)
6
- @endpoint = options.fetch(:endpoint)
7
- @md_file = options.fetch(:md_file, nil)
8
- @authentication = ensure_valid_auth_strategy(options.fetch(:authentication))
9
- @vendor = options.fetch(:vendor, {})
10
- @faraday_options = options.fetch(:faraday_options, {})
11
- @logger = options.fetch(:logger, nil)
12
-
13
- @connection = Faraday.new(@endpoint, @faraday_options) do |faraday|
7
+ @use_replication_endpoint = options.fetch(:use_replication_endpoint, false)
8
+ @endpoint = options.fetch(:endpoint)
9
+ @md_file = options.fetch(:md_file, nil)
10
+ @ds_file = options.fetch(:ds_file, nil)
11
+ @authentication = ensure_valid_auth_strategy(options.fetch(:authentication))
12
+ @vendor = options.fetch(:vendor, {})
13
+ @faraday_options = options.fetch(:faraday_options, {})
14
+ @logger = options.fetch(:logger, nil)
15
+ @md_cache = options.fetch(:md_cache, ResoTransport::MetadataCache)
16
+ @ds_cache = options.fetch(:ds_cache, ResoTransport::MetadataCache)
17
+ @connection = establish_connection
18
+ end
19
+
20
+ def establish_connection
21
+ Faraday.new(@endpoint, @faraday_options) do |faraday|
14
22
  faraday.request :url_encoded
15
23
  faraday.response :logger, @logger || ResoTransport.configuration.logger
16
- #yield faraday if block_given?
17
24
  faraday.use Authentication::Middleware, @authentication
18
- faraday.adapter Faraday.default_adapter #unless faraday.builder.send(:adapter_set?)
25
+ faraday.adapter Faraday.default_adapter # unless faraday.builder.send(:adapter_set?)
19
26
  end
20
27
  end
21
28
 
22
29
  def resources
23
- @resources ||= metadata.entity_sets.map {|es| {es.name => Resource.new(self, es)} }.reduce(:merge!)
30
+ @resources ||= metadata.entity_sets.map { |es| { es.name => resource_for(es) } }.reduce(:merge!)
31
+ end
32
+
33
+ def resource_for(entity_set)
34
+ localizations = {}
35
+ localizations = datasystem.localizations_for(entity_set.entity_type) if metadata.datasystem?
36
+
37
+ Resource.new(self, entity_set, localizations)
24
38
  end
25
39
 
26
40
  def metadata
27
41
  @metadata ||= Metadata.new(self)
28
42
  end
29
43
 
44
+ def datasystem
45
+ @datasystem ||= Datasystem.new(self)
46
+ end
47
+
30
48
  def to_s
31
- %(#<ResoTransport::Client endpoint="#{endpoint}", md_file="#{md_file}">)
49
+ %(#<ResoTransport::Client endpoint="#{endpoint}", md_file="#{md_file}", ds_file="#{ds_file}">)
32
50
  end
33
51
 
34
52
  def inspect
@@ -40,7 +58,7 @@ module ResoTransport
40
58
  def ensure_valid_auth_strategy(options)
41
59
  case options
42
60
  when Hash
43
- if options.has_key?(:endpoint)
61
+ if options.key?(:endpoint)
44
62
  Authentication::FetchTokenAuth.new(options)
45
63
  else
46
64
  Authentication::StaticTokenAuth.new(options)
@@ -49,6 +67,5 @@ module ResoTransport
49
67
  raise ArgumentError, "#{options.inspect} invalid: cannot determine strategy"
50
68
  end
51
69
  end
52
-
53
70
  end
54
71
  end
@@ -0,0 +1,25 @@
1
+ require_relative 'base_metadata'
2
+
3
+ module ResoTransport
4
+ class Datasystem < BaseMetadata
5
+ def initialize(client)
6
+ super client
7
+ @prefix = 'ds'
8
+ @classname = self.class.name
9
+ end
10
+
11
+ def localizations_for(resource_name)
12
+ localizations = parser.resources.dig(resource_name, 'Localizations') || []
13
+ localizations.map { |l| [l['Name'], l] }.to_h
14
+ end
15
+
16
+ def response
17
+ @response ||= client.connection.get('DataSystem') do |req|
18
+ req.headers['Accept'] = 'application/json'
19
+ @request = req
20
+ end
21
+ rescue Faraday::ConnectionFailed
22
+ raise NoResponse.new(request, nil, 'DataSystem')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module ResoTransport
2
+ class DatasystemParser
3
+ def parse(doc)
4
+ begin
5
+ data = doc.is_a?(File) ? doc.read : doc
6
+ @json = JSON.parse data
7
+ rescue JSON::ParserError => e
8
+ @json = {}
9
+ puts e.message
10
+ end
11
+ self
12
+ end
13
+
14
+ # value ->
15
+ # Resources ->
16
+ # Name ->
17
+ # ResourcePath ->
18
+ # Localizations ->
19
+ # Name ->
20
+ # ResourcePath ->
21
+
22
+ def resources
23
+ @resources ||= @json['value'].map { |v| v['Resources'] }.flatten.compact.map { |r| [r['Name'], r] }.to_h
24
+ end
25
+ end
26
+ end
@@ -1,11 +1,9 @@
1
1
  module ResoTransport
2
2
  EntitySet = Struct.new(:name, :schema, :entity_type) do
3
-
4
3
  def self.from_stream(args)
5
- schema, entity_type = ResoTransport.split_schema_and_class_name(args["EntityType"])
4
+ schema, entity_type = ResoTransport.split_schema_and_class_name(args['EntityType'])
6
5
 
7
- new(args["Name"], schema, entity_type)
6
+ new(args['Name'], schema, entity_type)
8
7
  end
9
-
10
8
  end
11
9
  end
@@ -1,30 +1,29 @@
1
1
  module ResoTransport
2
2
  EntityType = Struct.new(:name, :base_type, :primary_key, :schema) do
3
-
4
3
  def self.from_stream(args)
5
- new(args["Name"], args["BaseType"])
4
+ new(args['Name'], args['BaseType'])
6
5
  end
7
6
 
8
7
  def parse(record)
9
- record.each_pair do |k,v|
8
+ record.each_pair do |k, v|
10
9
  next if v.nil?
11
- if property = (property_map[k] || navigation_property_map[k])
12
- record[k] = property.parse(v)
13
- end
10
+
11
+ property = property_map[k] || navigation_property_map[k]
12
+ record[k] = property.parse(v) if property
14
13
  end
15
14
  end
16
15
 
17
16
  def parse_value(record)
18
- record.each_pair do |k,v|
17
+ record.each_pair do |k, v|
19
18
  next if v.nil?
20
- if property = (property_map[k] || navigation_property_map[k])
21
- record[k] = property.parse(v)
22
- end
19
+
20
+ property = property_map[k] || navigation_property_map[k]
21
+ record[k] = property.parse(v) if property
23
22
  end
24
23
  end
25
24
 
26
25
  def property_map
27
- @property_map ||= properties.inject({}) {|hsh, p| hsh[p.name] = p; hsh }
26
+ @property_map ||= properties.each_with_object({}) { |p, hsh| hsh[p.name] = p; }
28
27
  end
29
28
 
30
29
  def properties
@@ -32,7 +31,7 @@ module ResoTransport
32
31
  end
33
32
 
34
33
  def navigation_property_map
35
- @navigation_property_map ||= navigation_properties.inject({}) {|hsh, p| hsh[p.name] = p; hsh }
34
+ @navigation_property_map ||= navigation_properties.each_with_object({}) { |p, hsh| hsh[p.name] = p; }
36
35
  end
37
36
 
38
37
  def navigation_properties
@@ -42,6 +41,5 @@ module ResoTransport
42
41
  def enumerations
43
42
  @enumerations ||= []
44
43
  end
45
-
46
44
  end
47
45
  end
@@ -0,0 +1,69 @@
1
+ module ResoTransport
2
+ class ResourceError < StandardError
3
+ attr_reader :resource
4
+
5
+ def initialize(resource)
6
+ @resource = resource
7
+ super message
8
+ end
9
+
10
+ def resource_name
11
+ return resource.name if resource.respond_to?(:name)
12
+
13
+ resource || 'unknown'
14
+ end
15
+
16
+ def message
17
+ "Request error for #{resource_name}"
18
+ end
19
+ end
20
+
21
+ class EncodeError < ResourceError
22
+ def initialize(resource, property)
23
+ @property = property
24
+ super resource
25
+ end
26
+
27
+ def message
28
+ "Property #{@property} not found for #{resource_name}"
29
+ end
30
+ end
31
+
32
+ class LocalizationRequired < ResourceError
33
+ def message
34
+ "Localization required for #{resource_name}"
35
+ end
36
+ end
37
+
38
+ class RequestError < ResourceError
39
+ attr_reader :request, :response
40
+
41
+ def initialize(request, response, resource = nil)
42
+ @response = response.respond_to?(:to_hash) ? response.to_hash : response
43
+ @request = request
44
+ super resource
45
+ end
46
+
47
+ def message
48
+ "Received #{response[:status]} for #{resource_name}"
49
+ end
50
+ end
51
+
52
+ class NoResponse < RequestError
53
+ def message
54
+ "No response for #{resource_name}"
55
+ end
56
+ end
57
+
58
+ class ResponseError < RequestError
59
+ def message
60
+ "Request succeeded for #{resource_name} with response errors"
61
+ end
62
+ end
63
+
64
+ class AccessDenied < RequestError
65
+ def message
66
+ "Access denied: #{response.reason_phrase}"
67
+ end
68
+ end
69
+ end
@@ -1,10 +1,12 @@
1
- module ResoTransport
2
- Metadata = Struct.new(:client) do
1
+ require_relative 'base_metadata'
3
2
 
4
- MIME_TYPES = {
5
- xml: "application/xml",
6
- json: "application/json"
7
- }
3
+ module ResoTransport
4
+ class Metadata < BaseMetadata
5
+ def initialize(client)
6
+ super client
7
+ @prefix = 'md'
8
+ @classname = self.class.name
9
+ end
8
10
 
9
11
  def entity_sets
10
12
  parser.entity_sets
@@ -14,36 +16,17 @@ module ResoTransport
14
16
  parser.schemas
15
17
  end
16
18
 
17
- def parser
18
- @parser ||= MetadataParser.new.parse(get_data)
19
+ def datasystem?
20
+ parser.datasystem?
19
21
  end
20
22
 
21
- def get_data
22
- if client.md_file
23
- if File.exist?(client.md_file) && File.size(client.md_file) > 0
24
- File.new(client.md_file)
25
- else
26
- raw_md = raw
27
- File.open(client.md_file, "w") {|f| f.write(raw.force_encoding("UTF-8")) } unless raw_md.length == 0
28
- File.new(client.md_file)
29
- end
30
- else
31
- raw
32
- end
33
- end
34
-
35
- def raw
36
- resp = client.connection.get("$metadata") do |req|
23
+ def response
24
+ @response ||= client.connection.get('$metadata') do |req|
37
25
  req.headers['Accept'] = MIME_TYPES[client.vendor.fetch(:metadata_format, :xml).to_sym]
26
+ @request = req
38
27
  end
39
-
40
- if resp.success?
41
- resp.body
42
- else
43
- puts resp.body
44
- raise "Error getting metadata!"
45
- end
28
+ rescue Faraday::ConnectionFailed
29
+ raise NoResponse.new(request, nil, '$metadata')
46
30
  end
47
-
48
31
  end
49
32
  end
@@ -0,0 +1,20 @@
1
+ module ResoTransport
2
+ class MetadataCache
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def read
10
+ return nil if !File.exist?(name) || File.size(name).zero?
11
+
12
+ File.new(name)
13
+ end
14
+
15
+ def write(raw)
16
+ File.open(name, 'w') { |f| f.write(raw.force_encoding('UTF-8')) } if raw.length.positive?
17
+ File.new(name)
18
+ end
19
+ end
20
+ end