acfs 1.3.3 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +372 -0
- data/LICENSE +22 -0
- data/README.md +321 -0
- data/acfs.gemspec +38 -0
- data/lib/acfs.rb +51 -0
- data/lib/acfs/adapter/base.rb +26 -0
- data/lib/acfs/adapter/typhoeus.rb +82 -0
- data/lib/acfs/collection.rb +28 -0
- data/lib/acfs/collections/paginatable.rb +76 -0
- data/lib/acfs/configuration.rb +120 -0
- data/lib/acfs/errors.rb +147 -0
- data/lib/acfs/global.rb +101 -0
- data/lib/acfs/location.rb +76 -0
- data/lib/acfs/middleware/base.rb +24 -0
- data/lib/acfs/middleware/json.rb +31 -0
- data/lib/acfs/middleware/logger.rb +23 -0
- data/lib/acfs/middleware/msgpack.rb +32 -0
- data/lib/acfs/middleware/print.rb +23 -0
- data/lib/acfs/middleware/serializer.rb +41 -0
- data/lib/acfs/operation.rb +96 -0
- data/lib/acfs/request.rb +32 -0
- data/lib/acfs/request/callbacks.rb +54 -0
- data/lib/acfs/resource.rb +39 -0
- data/lib/acfs/resource/attributes.rb +270 -0
- data/lib/acfs/resource/attributes/base.rb +29 -0
- data/lib/acfs/resource/attributes/boolean.rb +39 -0
- data/lib/acfs/resource/attributes/date_time.rb +32 -0
- data/lib/acfs/resource/attributes/dict.rb +39 -0
- data/lib/acfs/resource/attributes/float.rb +33 -0
- data/lib/acfs/resource/attributes/integer.rb +29 -0
- data/lib/acfs/resource/attributes/list.rb +36 -0
- data/lib/acfs/resource/attributes/string.rb +26 -0
- data/lib/acfs/resource/attributes/uuid.rb +48 -0
- data/lib/acfs/resource/dirty.rb +37 -0
- data/lib/acfs/resource/initialization.rb +31 -0
- data/lib/acfs/resource/loadable.rb +35 -0
- data/lib/acfs/resource/locatable.rb +135 -0
- data/lib/acfs/resource/operational.rb +26 -0
- data/lib/acfs/resource/persistence.rb +258 -0
- data/lib/acfs/resource/query_methods.rb +266 -0
- data/lib/acfs/resource/service.rb +44 -0
- data/lib/acfs/resource/validation.rb +49 -0
- data/lib/acfs/response.rb +30 -0
- data/lib/acfs/response/formats.rb +27 -0
- data/lib/acfs/response/status.rb +33 -0
- data/lib/acfs/rspec.rb +13 -0
- data/lib/acfs/runner.rb +102 -0
- data/lib/acfs/service.rb +94 -0
- data/lib/acfs/service/middleware.rb +58 -0
- data/lib/acfs/service/middleware/stack.rb +65 -0
- data/lib/acfs/singleton_resource.rb +85 -0
- data/lib/acfs/stub.rb +199 -0
- data/lib/acfs/util.rb +22 -0
- data/lib/acfs/version.rb +16 -0
- data/lib/acfs/yard.rb +6 -0
- data/spec/acfs/adapter/typhoeus_spec.rb +55 -0
- data/spec/acfs/collection_spec.rb +157 -0
- data/spec/acfs/configuration_spec.rb +53 -0
- data/spec/acfs/global_spec.rb +140 -0
- data/spec/acfs/location_spec.rb +25 -0
- data/spec/acfs/middleware/json_spec.rb +79 -0
- data/spec/acfs/middleware/msgpack_spec.rb +62 -0
- data/spec/acfs/operation_spec.rb +12 -0
- data/spec/acfs/request/callbacks_spec.rb +48 -0
- data/spec/acfs/request_spec.rb +79 -0
- data/spec/acfs/resource/attributes/boolean_spec.rb +58 -0
- data/spec/acfs/resource/attributes/date_time_spec.rb +51 -0
- data/spec/acfs/resource/attributes/dict_spec.rb +77 -0
- data/spec/acfs/resource/attributes/float_spec.rb +61 -0
- data/spec/acfs/resource/attributes/integer_spec.rb +36 -0
- data/spec/acfs/resource/attributes/list_spec.rb +60 -0
- data/spec/acfs/resource/attributes/uuid_spec.rb +42 -0
- data/spec/acfs/resource/attributes_spec.rb +179 -0
- data/spec/acfs/resource/dirty_spec.rb +49 -0
- data/spec/acfs/resource/initialization_spec.rb +36 -0
- data/spec/acfs/resource/loadable_spec.rb +22 -0
- data/spec/acfs/resource/locatable_spec.rb +118 -0
- data/spec/acfs/resource/persistance_spec.rb +322 -0
- data/spec/acfs/resource/query_methods_spec.rb +548 -0
- data/spec/acfs/resource/validation_spec.rb +129 -0
- data/spec/acfs/response/formats_spec.rb +52 -0
- data/spec/acfs/response/status_spec.rb +71 -0
- data/spec/acfs/runner_spec.rb +95 -0
- data/spec/acfs/service/middleware_spec.rb +35 -0
- data/spec/acfs/service_spec.rb +48 -0
- data/spec/acfs/singleton_resource_spec.rb +17 -0
- data/spec/acfs/stub_spec.rb +345 -0
- data/spec/acfs_spec.rb +205 -0
- data/spec/fixtures/config.yml +14 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/hash.rb +11 -0
- data/spec/support/response.rb +12 -0
- data/spec/support/service.rb +92 -0
- data/spec/support/shared/find_callbacks.rb +50 -0
- metadata +159 -26
data/acfs.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'acfs/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'acfs'
|
9
|
+
spec.version = Acfs::VERSION
|
10
|
+
spec.authors = ['Jan Graichen']
|
11
|
+
spec.email = %w[jgraichen@altimos.de]
|
12
|
+
spec.homepage = 'https://github.com/jgraichen/acfs'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.description = 'API Client For Services'
|
15
|
+
spec.summary = <<~SUMMARY.strip
|
16
|
+
An abstract API base client for service oriented application.
|
17
|
+
SUMMARY
|
18
|
+
|
19
|
+
spec.files = Dir['**/*'].grep(%r{
|
20
|
+
^((bin|lib|test|spec|features)/|
|
21
|
+
.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)
|
22
|
+
}xi)
|
23
|
+
|
24
|
+
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
|
25
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
|
+
spec.require_paths = %w[lib]
|
27
|
+
|
28
|
+
spec.required_ruby_version = '>= 2.5.0'
|
29
|
+
|
30
|
+
spec.add_runtime_dependency 'actionpack', '>= 5.2'
|
31
|
+
spec.add_runtime_dependency 'activemodel', '>= 5.2'
|
32
|
+
spec.add_runtime_dependency 'activesupport', '>= 5.2'
|
33
|
+
spec.add_runtime_dependency 'multi_json', '~> 1.0'
|
34
|
+
spec.add_runtime_dependency 'rack'
|
35
|
+
spec.add_runtime_dependency 'typhoeus', '~> 1.0'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'bundler'
|
38
|
+
end
|
data/lib/acfs.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/hash'
|
5
|
+
require 'active_support/core_ext/class'
|
6
|
+
require 'active_support/core_ext/string'
|
7
|
+
require 'active_support/core_ext/module'
|
8
|
+
require 'active_support/notifications'
|
9
|
+
|
10
|
+
module Acfs
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
require 'acfs/version'
|
13
|
+
require 'acfs/errors'
|
14
|
+
require 'acfs/global'
|
15
|
+
require 'acfs/util'
|
16
|
+
|
17
|
+
require 'acfs/collection'
|
18
|
+
require 'acfs/configuration'
|
19
|
+
require 'acfs/location'
|
20
|
+
require 'acfs/operation'
|
21
|
+
require 'acfs/request'
|
22
|
+
require 'acfs/resource'
|
23
|
+
require 'acfs/response'
|
24
|
+
require 'acfs/runner'
|
25
|
+
require 'acfs/service'
|
26
|
+
require 'acfs/singleton_resource'
|
27
|
+
|
28
|
+
extend Global
|
29
|
+
|
30
|
+
autoload :Stub
|
31
|
+
|
32
|
+
module Middleware
|
33
|
+
extend ActiveSupport::Autoload
|
34
|
+
require 'acfs/middleware/base'
|
35
|
+
require 'acfs/middleware/serializer'
|
36
|
+
|
37
|
+
autoload :Print
|
38
|
+
autoload :Logger
|
39
|
+
autoload :JSON
|
40
|
+
autoload :JsonDecoder, 'acfs/middleware/json'
|
41
|
+
autoload :JsonEncoder, 'acfs/middleware/json'
|
42
|
+
autoload :MessagePack, 'acfs/middleware/msgpack'
|
43
|
+
autoload :MessagePackDecoder, 'acfs/middleware/msgpack'
|
44
|
+
autoload :MessagePackEncoder, 'acfs/middleware/msgpack'
|
45
|
+
end
|
46
|
+
|
47
|
+
module Adapter
|
48
|
+
require 'acfs/adapter/base'
|
49
|
+
require 'acfs/adapter/typhoeus'
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Adapter
|
4
|
+
# Base adapter handling operation queuing
|
5
|
+
# and processing.
|
6
|
+
#
|
7
|
+
class Base
|
8
|
+
def initialize(*); end
|
9
|
+
|
10
|
+
# Start processing queued requests.
|
11
|
+
#
|
12
|
+
def start; end
|
13
|
+
|
14
|
+
# Abort running and queued requests.
|
15
|
+
#
|
16
|
+
def abort; end
|
17
|
+
|
18
|
+
# Run request right now skipping queue.
|
19
|
+
#
|
20
|
+
def run(_); end
|
21
|
+
|
22
|
+
# Enqueue request to be run later.
|
23
|
+
#
|
24
|
+
def queue(_); end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
|
5
|
+
module Acfs
|
6
|
+
module Adapter
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
tcp_keepalive: true,
|
9
|
+
tcp_keepidle: 5,
|
10
|
+
tcp_keepintvl: 5
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
# Adapter for Typhoeus.
|
14
|
+
#
|
15
|
+
class Typhoeus < Base
|
16
|
+
def initialize(**kwargs)
|
17
|
+
super
|
18
|
+
|
19
|
+
@opts = DEFAULT_OPTIONS
|
20
|
+
@opts = @opts.merge(opts) if (opts = kwargs.delete(:opts))
|
21
|
+
@kwargs = kwargs
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
hydra.run
|
26
|
+
rescue StandardError
|
27
|
+
@hydra = nil
|
28
|
+
raise
|
29
|
+
end
|
30
|
+
|
31
|
+
delegate :abort, to: :hydra
|
32
|
+
|
33
|
+
def run(request)
|
34
|
+
convert_request(request).run
|
35
|
+
end
|
36
|
+
|
37
|
+
def queue(request)
|
38
|
+
hydra.queue convert_request request
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def hydra
|
44
|
+
@hydra ||= ::Typhoeus::Hydra.new(**@kwargs)
|
45
|
+
end
|
46
|
+
|
47
|
+
def convert_request(req)
|
48
|
+
opts = {
|
49
|
+
method: req.method,
|
50
|
+
params: req.params,
|
51
|
+
headers: req.headers.merge(
|
52
|
+
'Expect' => '',
|
53
|
+
'Transfer-Encoding' => ''
|
54
|
+
),
|
55
|
+
body: req.body
|
56
|
+
}
|
57
|
+
|
58
|
+
request = ::Typhoeus::Request.new(req.url, **@opts, **opts)
|
59
|
+
|
60
|
+
request.on_complete do |response|
|
61
|
+
raise ::Acfs::TimeoutError.new(req) if response.timed_out?
|
62
|
+
|
63
|
+
if response.code.zero?
|
64
|
+
# Failed to get HTTP response
|
65
|
+
raise ::Acfs::RequestError.new(req, response.return_message)
|
66
|
+
end
|
67
|
+
|
68
|
+
req.complete! convert_response(req, response)
|
69
|
+
end
|
70
|
+
|
71
|
+
request
|
72
|
+
end
|
73
|
+
|
74
|
+
def convert_response(request, response)
|
75
|
+
Acfs::Response.new request,
|
76
|
+
status: response.code,
|
77
|
+
headers: response.headers,
|
78
|
+
body: response.body
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'acfs/resource/loadable'
|
6
|
+
require 'acfs/collections/paginatable'
|
7
|
+
|
8
|
+
module Acfs
|
9
|
+
class Collection < ::Delegator
|
10
|
+
include Resource::Loadable
|
11
|
+
include Acfs::Util::Callbacks
|
12
|
+
include Collections::Paginatable
|
13
|
+
|
14
|
+
def initialize(resource_class)
|
15
|
+
super([])
|
16
|
+
|
17
|
+
@resource_class = resource_class
|
18
|
+
end
|
19
|
+
|
20
|
+
def __getobj__
|
21
|
+
@models
|
22
|
+
end
|
23
|
+
|
24
|
+
def __setobj__(obj)
|
25
|
+
@models = obj
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Collections
|
4
|
+
module Paginatable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
def self.operation(_action, **opts, &_block)
|
9
|
+
opts[:url]
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :total_pages, :current_page, :total_count
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_response(response)
|
16
|
+
setup_params response.request.params if response.request
|
17
|
+
setup_headers response.headers
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_page(&block)
|
21
|
+
page 'next', &block
|
22
|
+
end
|
23
|
+
|
24
|
+
def prev_page(&block)
|
25
|
+
page 'prev', &block
|
26
|
+
end
|
27
|
+
|
28
|
+
def first_page(&block)
|
29
|
+
page 'first', &block
|
30
|
+
end
|
31
|
+
|
32
|
+
def last_page(&block)
|
33
|
+
page 'last', &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def page(rel, &block)
|
37
|
+
return unless relations[rel]
|
38
|
+
|
39
|
+
@resource_class.all nil, url: relations[rel], &block
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def relations
|
45
|
+
@relations ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup_headers(headers)
|
49
|
+
if headers['X-Total-Pages']
|
50
|
+
@total_pages = Integer(headers['X-Total-Pages'])
|
51
|
+
end
|
52
|
+
|
53
|
+
if headers['X-Total-Count']
|
54
|
+
@total_count = Integer(headers['X-Total-Count'])
|
55
|
+
end
|
56
|
+
|
57
|
+
setup_links headers['Link'] if headers['Link']
|
58
|
+
end
|
59
|
+
|
60
|
+
def setup_links(links)
|
61
|
+
links.split(/,\s+/).each do |link|
|
62
|
+
if link =~ /^\s*<([^>]+)>.*\s+rel="([\w_-]+)".*$/
|
63
|
+
relations[Regexp.last_match[2]] = Regexp.last_match[1]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def setup_params(params)
|
69
|
+
@current_page = begin
|
70
|
+
Integer params.fetch(:page, 1)
|
71
|
+
rescue ArgumentError
|
72
|
+
params[:page]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Acfs
|
7
|
+
# Acfs configuration is used to locate services and get their base URLs.
|
8
|
+
#
|
9
|
+
class Configuration
|
10
|
+
attr_reader :locations
|
11
|
+
attr_accessor :adapter
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def initialize
|
15
|
+
@locations = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api public
|
19
|
+
#
|
20
|
+
# Configure using given block. If block accepts zero arguments
|
21
|
+
# bock will be evaluated in context of the configuration instance
|
22
|
+
# otherwise the configuration instance will be given as first arguments.
|
23
|
+
#
|
24
|
+
# @yield [configuration] Give configuration as arguments or evaluate block
|
25
|
+
# in context of configuration object.
|
26
|
+
# @yieldparam configuration [Configuration] Configuration object.
|
27
|
+
# @return [undefined]
|
28
|
+
#
|
29
|
+
def configure(&block)
|
30
|
+
if block.arity.positive?
|
31
|
+
block.call self
|
32
|
+
else
|
33
|
+
instance_eval(&block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api public
|
38
|
+
#
|
39
|
+
# @overload locate(service, uri)
|
40
|
+
# Configures URL where a service can be reached.
|
41
|
+
#
|
42
|
+
# @param [Symbol] service
|
43
|
+
# Service identity key for service that is reachable under given URL.
|
44
|
+
#
|
45
|
+
# @param [String] uri
|
46
|
+
# URL where service is reachable. Will be passed to {URI.parse}.
|
47
|
+
#
|
48
|
+
# @return [undefined]
|
49
|
+
#
|
50
|
+
# @overload locate(service)
|
51
|
+
# Return configured base URL for given service identity key.
|
52
|
+
#
|
53
|
+
# @param [Symbol] service Service identity key to lookup.
|
54
|
+
# @return [URI, NilClass] Configured base URL or nil.
|
55
|
+
#
|
56
|
+
def locate(service, uri = nil)
|
57
|
+
service = service.to_s.underscore.to_sym
|
58
|
+
if uri.nil?
|
59
|
+
locations[service]
|
60
|
+
else
|
61
|
+
locations[service] = URI.parse uri
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api public
|
66
|
+
#
|
67
|
+
# Load configuration from given YAML file.
|
68
|
+
#
|
69
|
+
# @param [String] filename Path to YAML configuration file.
|
70
|
+
# @return [undefined]
|
71
|
+
#
|
72
|
+
def load(filename)
|
73
|
+
config = YAML.safe_load(File.read(filename), [], [], true)
|
74
|
+
env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
|
75
|
+
|
76
|
+
config = config[env] if config.key? env
|
77
|
+
config.each do |key, value|
|
78
|
+
case key
|
79
|
+
when 'services' then load_services value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# @api private
|
85
|
+
#
|
86
|
+
# Load services from configuration YAML.
|
87
|
+
#
|
88
|
+
def load_services(services)
|
89
|
+
services.each do |service, data|
|
90
|
+
if (val = data).is_a?(String) || (val = data['locate'])
|
91
|
+
locate service.to_sym, val
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class << self
|
97
|
+
# @api private
|
98
|
+
#
|
99
|
+
# Return current configuration object.
|
100
|
+
#
|
101
|
+
# @return [Configuration]
|
102
|
+
#
|
103
|
+
def current
|
104
|
+
@current ||= new
|
105
|
+
end
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
#
|
109
|
+
# Swap configuration object with given new one. Must be
|
110
|
+
# a {Configuration} object.
|
111
|
+
#
|
112
|
+
# @param [Configuration] configuration
|
113
|
+
# @return [undefined]
|
114
|
+
#
|
115
|
+
def set(configuration)
|
116
|
+
@current = configuration if configuration.is_a? Configuration
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/acfs/errors.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs
|
4
|
+
# Acfs base error.
|
5
|
+
#
|
6
|
+
class Error < StandardError
|
7
|
+
def initialize(opts = {}, message = nil)
|
8
|
+
opts.merge! message: message if message
|
9
|
+
super opts[:message]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class UnsupportedOperation < StandardError; end
|
14
|
+
|
15
|
+
class RequestError < Error
|
16
|
+
attr_reader :request
|
17
|
+
|
18
|
+
def initialize(request, message)
|
19
|
+
@request = request
|
20
|
+
|
21
|
+
message = "#{message}: #{request.method.upcase} #{request.url}"
|
22
|
+
|
23
|
+
super message: message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class TimeoutError < RequestError
|
28
|
+
def initialize(request)
|
29
|
+
super(request, 'Timeout reached')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Response error containing the responsible response object.
|
34
|
+
#
|
35
|
+
class ErroneousResponse < Error
|
36
|
+
attr_reader :response
|
37
|
+
|
38
|
+
def initialize(opts = {})
|
39
|
+
@response = opts[:response]
|
40
|
+
|
41
|
+
message = if response
|
42
|
+
(opts[:message] ? "#{opts[:message]}:" : 'Received') +
|
43
|
+
" #{response.code} for #{response.request.method.upcase}" \
|
44
|
+
" #{response.request.url} #{response.request.format}"
|
45
|
+
else
|
46
|
+
opts[:message] || 'Received erroneous response'
|
47
|
+
end
|
48
|
+
|
49
|
+
super opts, message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class AmbiguousStubError < Error
|
54
|
+
attr_reader :stubs, :operation
|
55
|
+
|
56
|
+
def initialize(opts = {})
|
57
|
+
require 'pp'
|
58
|
+
|
59
|
+
@stubs = opts.delete :stubs
|
60
|
+
@operation = opts.delete :operation
|
61
|
+
|
62
|
+
message = "Ambiguous stubs for #{operation.action} " \
|
63
|
+
"on #{operation.resource}.\n" +
|
64
|
+
stubs.map {|s| " #{s.opts.pretty_inspect}" }.join
|
65
|
+
|
66
|
+
super opts, message
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# 400
|
71
|
+
class BadRequest < ErroneousResponse; end
|
72
|
+
|
73
|
+
# 401
|
74
|
+
class Unauthorized < ErroneousResponse; end
|
75
|
+
|
76
|
+
# 403
|
77
|
+
class Forbidden < ErroneousResponse; end
|
78
|
+
|
79
|
+
# 404
|
80
|
+
class ResourceNotFound < ErroneousResponse; end
|
81
|
+
|
82
|
+
# 422
|
83
|
+
class InvalidResource < ErroneousResponse
|
84
|
+
attr_reader :errors, :resource
|
85
|
+
|
86
|
+
def initialize(opts = {})
|
87
|
+
@errors = opts.delete :errors
|
88
|
+
@resource = opts.delete :resource
|
89
|
+
|
90
|
+
case @errors
|
91
|
+
when Hash
|
92
|
+
opts[:message] ||= @errors.each_pair.map do |k, v|
|
93
|
+
@errors.is_a?(Array) ? "#{k}: #{v.join(', ')}" : "#{k}: #{v}"
|
94
|
+
end.join ', '
|
95
|
+
when Array
|
96
|
+
opts[:message] ||= @errors.join ', '
|
97
|
+
end
|
98
|
+
|
99
|
+
super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# 500
|
104
|
+
class ServerError < ErroneousResponse; end
|
105
|
+
|
106
|
+
# 502
|
107
|
+
class BadGateway < ErroneousResponse; end
|
108
|
+
|
109
|
+
# 503
|
110
|
+
class ServiceUnavailable < ErroneousResponse; end
|
111
|
+
|
112
|
+
# 504
|
113
|
+
class GatewayTimeout < ErroneousResponse; end
|
114
|
+
|
115
|
+
# A ResourceNotLoaded error will be thrown when calling some
|
116
|
+
# modifing methods on not loaded resources as it is usally
|
117
|
+
# unwanted to call e.g. `update_attributes` on a not loaded
|
118
|
+
# resource.
|
119
|
+
# Correct solution is to first run `Acfs.run` to fetch the
|
120
|
+
# resource and then update the resource.
|
121
|
+
#
|
122
|
+
class ResourceNotLoaded < Error
|
123
|
+
attr_reader :resource
|
124
|
+
|
125
|
+
def initialize(opts = {})
|
126
|
+
@resource = opts.delete :resource
|
127
|
+
super
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Gets raised if ressource type is no valid subclass of
|
132
|
+
# parent resource. Check if the type is set to the correct
|
133
|
+
# Acfs::Resource Name
|
134
|
+
class ResourceTypeError < Error
|
135
|
+
attr_reader :base_class, :type_name
|
136
|
+
|
137
|
+
def initialize(opts = {})
|
138
|
+
@base_class = opts.delete :base_class
|
139
|
+
@type_name = opts.delete :type_name
|
140
|
+
opts[:message] = "Received resource type `#{type_name}` " \
|
141
|
+
"is no subclass of #{base_class}"
|
142
|
+
super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class RealRequestsNotAllowedError < StandardError; end
|
147
|
+
end
|