stub_requests 0.1.9 → 0.1.10
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 +4 -4
- data/.reek.yml +5 -9
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +23 -0
- data/README.md +9 -9
- data/Rakefile +7 -5
- data/lib/stub_requests.rb +30 -16
- data/lib/stub_requests/api.rb +45 -26
- data/lib/stub_requests/callback.rb +3 -1
- data/lib/stub_requests/callback_registry.rb +9 -55
- data/lib/stub_requests/concerns/argument_validation.rb +47 -0
- data/lib/stub_requests/concerns/property.rb +114 -0
- data/lib/stub_requests/concerns/property/validator.rb +137 -0
- data/lib/stub_requests/concerns/register_verb.rb +110 -0
- data/lib/stub_requests/configuration.rb +19 -2
- data/lib/stub_requests/dsl.rb +5 -6
- data/lib/stub_requests/dsl/method_definition.rb +2 -8
- data/lib/stub_requests/endpoint.rb +22 -23
- data/lib/stub_requests/endpoint_registry.rb +157 -0
- data/lib/stub_requests/exceptions.rb +28 -10
- data/lib/stub_requests/request_stub.rb +29 -14
- data/lib/stub_requests/service.rb +55 -7
- data/lib/stub_requests/service_registry.rb +30 -79
- data/lib/stub_requests/stub_registry.rb +22 -80
- data/lib/stub_requests/stub_requests.rb +8 -5
- data/lib/stub_requests/uri.rb +0 -17
- data/lib/stub_requests/utils/fuzzy.rb +70 -0
- data/lib/stub_requests/version.rb +1 -1
- data/lib/stub_requests/webmock/builder.rb +9 -51
- data/lib/stub_requests/webmock/stub_registry_extension.rb +1 -1
- data/lib/tasks/changelog.rake +1 -7
- data/stub_requests.gemspec +1 -0
- data/update_docs.sh +2 -2
- metadata +27 -8
- data/lib/stub_requests/argument_validation.rb +0 -48
- data/lib/stub_requests/endpoint_stub.rb +0 -89
- data/lib/stub_requests/endpoints.rb +0 -246
- data/lib/stub_requests/hash_util.rb +0 -32
- data/lib/stub_requests/observable.rb +0 -18
- data/lib/stub_requests/property.rb +0 -112
- data/lib/stub_requests/property/validator.rb +0 -135
@@ -13,10 +13,12 @@ module StubRequests
|
|
13
13
|
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
14
|
#
|
15
15
|
class Endpoint
|
16
|
-
|
17
|
-
|
16
|
+
# includes "Comparable"
|
17
|
+
# @!parse include Comparable
|
18
18
|
include Comparable
|
19
|
-
|
19
|
+
# includes "Concerns::Property"
|
20
|
+
# @!parse include Concerns::Property
|
21
|
+
include Concerns::Property
|
20
22
|
#
|
21
23
|
# @!attribute [rw] id
|
22
24
|
# @return [Symbol] the id of the endpoint
|
@@ -29,49 +31,46 @@ module StubRequests
|
|
29
31
|
# @!attribute [rw] path
|
30
32
|
# @return [String] a string template for the endpoint
|
31
33
|
property :path, type: String
|
32
|
-
|
33
34
|
#
|
34
|
-
# @!attribute [rw]
|
35
|
-
# @
|
36
|
-
|
37
|
-
attr_reader :service
|
38
|
-
|
35
|
+
# @!attribute [rw] uri
|
36
|
+
# @return [String] the full uri for the endpoint
|
37
|
+
attr_reader :uri
|
39
38
|
#
|
40
39
|
# @!attribute [rw] service_id
|
41
40
|
# @see
|
42
41
|
# @return [Symbol] the id of the service
|
43
42
|
attr_reader :service_id
|
44
|
-
|
45
43
|
#
|
46
44
|
# @!attribute [rw] service_uri
|
47
45
|
# @see
|
48
46
|
# @return [String] a service's base URI
|
49
47
|
attr_reader :service_uri
|
50
|
-
|
51
48
|
#
|
52
|
-
# @!attribute [rw]
|
49
|
+
# @!attribute [rw] route_params
|
53
50
|
# @see
|
54
51
|
# @return [Array<Symbol>] an array with required route params
|
55
52
|
attr_reader :route_params
|
56
53
|
|
57
54
|
#
|
58
|
-
#
|
55
|
+
# Initialize an endpoint for a specific {Service}
|
59
56
|
#
|
60
57
|
#
|
61
|
-
# @param [Service] service a registered service
|
62
58
|
# @param [Symbol] endpoint_id a descriptive id for the endpoint
|
59
|
+
# @param [Symbol] service_id the id of a registered service
|
60
|
+
# @param [String] service_uri the uri of a registered service
|
63
61
|
# @param [Symbol] verb a HTTP verb
|
64
62
|
# @param [String] path how to reach the endpoint
|
65
63
|
#
|
66
|
-
def initialize(
|
67
|
-
self.id
|
68
|
-
self.verb
|
69
|
-
self.path
|
64
|
+
def initialize(endpoint_id:, service_id:, service_uri:, verb:, path:)
|
65
|
+
self.id = endpoint_id
|
66
|
+
self.verb = verb
|
67
|
+
self.path = path
|
70
68
|
|
71
|
-
@
|
72
|
-
@
|
73
|
-
@
|
69
|
+
@service_id = service_id
|
70
|
+
@service_uri = service_uri
|
71
|
+
@uri = URI.safe_join(service_uri, path)
|
74
72
|
@route_params = URI.route_params(path)
|
73
|
+
@stubs = Concurrent::Array.new
|
75
74
|
end
|
76
75
|
|
77
76
|
#
|
@@ -84,8 +83,8 @@ module StubRequests
|
|
84
83
|
# @return [Endpoint] returns the updated endpoint
|
85
84
|
#
|
86
85
|
def update(verb, path)
|
87
|
-
|
88
|
-
|
86
|
+
@verb = verb
|
87
|
+
@path = path
|
89
88
|
self
|
90
89
|
end
|
91
90
|
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Class Endpoints manages a collection of endpoints
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
#
|
15
|
+
class EndpointRegistry
|
16
|
+
# extend "Forwardable"
|
17
|
+
# @!parse extend Forwardable
|
18
|
+
extend Forwardable
|
19
|
+
|
20
|
+
# includes "Singleton"
|
21
|
+
# @!parse include Singleton
|
22
|
+
include Singleton
|
23
|
+
# includes "Enumerable"
|
24
|
+
# @!parse include Enumerable
|
25
|
+
include Enumerable
|
26
|
+
|
27
|
+
delegate [:each, :[], :[]=, :delete, :keys, :values] => :endpoints
|
28
|
+
|
29
|
+
#
|
30
|
+
# Return all endpoints for a service
|
31
|
+
#
|
32
|
+
# @param [Symbol] service_id the id of a registered service
|
33
|
+
#
|
34
|
+
# @return [Array<Endpoint>] the endpoints for the service
|
35
|
+
#
|
36
|
+
def self.[](service_id)
|
37
|
+
instance.values.select { |ep| ep.service_id == service_id }
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# @!attribute [rw] endpoints
|
42
|
+
# @return [Concurrent::Map<Symbol, Endpoint>] a map with endpoints
|
43
|
+
attr_reader :endpoints
|
44
|
+
|
45
|
+
#
|
46
|
+
# Initialize is used by Singleton
|
47
|
+
#
|
48
|
+
#
|
49
|
+
def initialize
|
50
|
+
reset
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Resets the endpoints array (used for testing)
|
55
|
+
#
|
56
|
+
#
|
57
|
+
def reset
|
58
|
+
@endpoints = Concurrent::Map.new
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# The size of the endpoints array
|
63
|
+
#
|
64
|
+
#
|
65
|
+
# @return [Integer]
|
66
|
+
#
|
67
|
+
def size
|
68
|
+
keys.size
|
69
|
+
end
|
70
|
+
alias count size
|
71
|
+
|
72
|
+
#
|
73
|
+
# Registers an endpoint in the collection
|
74
|
+
#
|
75
|
+
# @param [Endpoint] endpoint the endpoint to register
|
76
|
+
#
|
77
|
+
# @return [Endpoint]
|
78
|
+
#
|
79
|
+
def register(endpoint)
|
80
|
+
StubRequests.logger.warn("Endpoint already registered: #{endpoint}") if find(endpoint)
|
81
|
+
|
82
|
+
self[endpoint.id] = endpoint
|
83
|
+
endpoint
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Fetches an endpoint from the collection or raises an error
|
88
|
+
#
|
89
|
+
#
|
90
|
+
# @param [Symbol] endpoint_id the id of the endpoint
|
91
|
+
#
|
92
|
+
# @raise [EndpointNotFound] when an endpoint couldn't be found
|
93
|
+
#
|
94
|
+
# @return [Endpoint]
|
95
|
+
#
|
96
|
+
def find!(endpoint_id)
|
97
|
+
endpoint = find(endpoint_id)
|
98
|
+
return endpoint if endpoint
|
99
|
+
|
100
|
+
raise EndpointNotFound, id: endpoint_id, suggestions: suggestions(endpoint_id)
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Fetches an endpoint from the collection or raises an error
|
105
|
+
#
|
106
|
+
|
107
|
+
# @param [Symbol] endpoint_id the id of the endpoint
|
108
|
+
#
|
109
|
+
# @return [Endpoint, nil]
|
110
|
+
|
111
|
+
def find(endpoint_id)
|
112
|
+
endpoint_id = endpoint_id.id if endpoint_id.is_a?(Endpoint)
|
113
|
+
self[endpoint_id]
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Returns an array of potential alternatives
|
118
|
+
#
|
119
|
+
# @param [Symbol] endpoint_id the id of an endpoint
|
120
|
+
#
|
121
|
+
# @return [Array<Symbol>] an array of endpoint_id's
|
122
|
+
#
|
123
|
+
def suggestions(endpoint_id)
|
124
|
+
Utils::Fuzzy.match(endpoint_id, keys)
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# Returns a descriptive string with all endpoints in the collection
|
129
|
+
#
|
130
|
+
#
|
131
|
+
# @return [String]
|
132
|
+
#
|
133
|
+
def to_s
|
134
|
+
[
|
135
|
+
+"#<#{self.class} endpoints=",
|
136
|
+
+endpoints_string,
|
137
|
+
+">",
|
138
|
+
].join("")
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Returns a nicely formatted string with an array of endpoints
|
143
|
+
#
|
144
|
+
#
|
145
|
+
# @return [<type>] <description>
|
146
|
+
#
|
147
|
+
def endpoints_string
|
148
|
+
"[#{endpoints_as_string}]"
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def endpoints_as_string
|
154
|
+
endpoints.values.map(&:to_s).join(",") if endpoints.size.positive?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -15,7 +15,34 @@ module StubRequests
|
|
15
15
|
#
|
16
16
|
# EndpointNotFound is raised when an endpoint cannot be found
|
17
17
|
#
|
18
|
-
class EndpointNotFound < Error
|
18
|
+
class EndpointNotFound < Error
|
19
|
+
attr_reader :id
|
20
|
+
|
21
|
+
def initialize(id:, suggestions: [])
|
22
|
+
@id = id
|
23
|
+
@suggestions = Array(suggestions).compact
|
24
|
+
error_message = [base_message, suggestions_message].join(".")
|
25
|
+
super(error_message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def base_message
|
29
|
+
@base_message ||= "Couldn't find an endpoint with id=:#{id}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def suggestions_message
|
33
|
+
return if suggestions.none?
|
34
|
+
|
35
|
+
@suggestions_message ||= " Did you mean one of the following? (#{suggestions_string})"
|
36
|
+
end
|
37
|
+
|
38
|
+
def suggestions
|
39
|
+
@suggestions.map { |sym| ":#{sym}" }
|
40
|
+
end
|
41
|
+
|
42
|
+
def suggestions_string
|
43
|
+
suggestions.join(", ")
|
44
|
+
end
|
45
|
+
end
|
19
46
|
|
20
47
|
#
|
21
48
|
# Class InvalidCallback is raised when a callback argument doesn't have the correct number of arguments
|
@@ -55,15 +82,6 @@ module StubRequests
|
|
55
82
|
end
|
56
83
|
end
|
57
84
|
|
58
|
-
#
|
59
|
-
# ServiceHaveEndpoints is raised to prevent overwriting a registered service's endpoints
|
60
|
-
#
|
61
|
-
class ServiceHaveEndpoints < StandardError
|
62
|
-
def initialize(service)
|
63
|
-
super("Service with id #{service.id} have already been registered. #{service}")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
85
|
#
|
68
86
|
# ServiceNotFound is raised when a service cannot be found
|
69
87
|
#
|
@@ -14,15 +14,20 @@ module StubRequests
|
|
14
14
|
# @since 0.1.2
|
15
15
|
#
|
16
16
|
class RequestStub
|
17
|
-
|
17
|
+
# extends "Forwardable"
|
18
|
+
# @!parse extend Forwardable
|
18
19
|
extend Forwardable
|
19
20
|
|
21
|
+
# includes "Concerns::Property"
|
22
|
+
# @!parse include Concerns::Property
|
23
|
+
include Concerns::Property
|
24
|
+
|
20
25
|
# Delegate service_id, endpoint_id, verb and path to endpoint
|
21
|
-
delegate [:service_id, :
|
26
|
+
delegate [:service_id, :service_uri, :verb, :path] => :endpoint
|
22
27
|
#
|
23
28
|
# @!attribute [r] endpoint
|
24
|
-
# @return [
|
25
|
-
property :
|
29
|
+
# @return [Symbol] the id of a registered {Endpoint}
|
30
|
+
property :endpoint_id, type: Symbol
|
26
31
|
#
|
27
32
|
# @!attribute [r] verb
|
28
33
|
# @return [Symbol] a HTTP verb/method
|
@@ -30,11 +35,11 @@ module StubRequests
|
|
30
35
|
#
|
31
36
|
# @!attribute [r] uri
|
32
37
|
# @return [String] the full URI for this endpoint
|
33
|
-
property :
|
38
|
+
property :request_uri, type: String
|
34
39
|
#
|
35
|
-
# @!attribute [r]
|
40
|
+
# @!attribute [r] webmock_stub
|
36
41
|
# @return [WebMock::RequestStub] a webmock stubbed request
|
37
|
-
property :
|
42
|
+
property :webmock_stub, type: WebMock::RequestStub
|
38
43
|
#
|
39
44
|
# @!attribute [r] recorded_at
|
40
45
|
# @return [Time] the time this record was recorded
|
@@ -52,20 +57,30 @@ module StubRequests
|
|
52
57
|
# Initialize a new Record
|
53
58
|
#
|
54
59
|
#
|
55
|
-
# @param [Endpoint]
|
56
|
-
# @param [WebMock::RequestStub]
|
60
|
+
# @param [Endpoint] endpoint_id the id of a stubbed endpoint
|
61
|
+
# @param [WebMock::RequestStub] webmock_stub the stubbed webmock request
|
57
62
|
#
|
58
|
-
def initialize(
|
59
|
-
request_pattern
|
60
|
-
self.
|
63
|
+
def initialize(endpoint_id, webmock_stub)
|
64
|
+
request_pattern = webmock_stub.request_pattern
|
65
|
+
self.endpoint_id = endpoint_id
|
61
66
|
self.verb = request_pattern.method_pattern.to_s.to_sym
|
62
|
-
self.
|
63
|
-
self.
|
67
|
+
self.request_uri = request_pattern.uri_pattern.to_s
|
68
|
+
self.webmock_stub = webmock_stub
|
64
69
|
self.recorded_at = Time.now
|
65
70
|
self.recorded_from = RSpec.current_example.metadata[:location]
|
66
71
|
@responded_at = nil # ByPass the validation for the initializer
|
67
72
|
end
|
68
73
|
|
74
|
+
#
|
75
|
+
# Retrieve the endpoint for this request stub
|
76
|
+
#
|
77
|
+
#
|
78
|
+
# @return [Endpoint] <description>
|
79
|
+
#
|
80
|
+
def endpoint
|
81
|
+
EndpointRegistry.instance[endpoint_id]
|
82
|
+
end
|
83
|
+
|
69
84
|
#
|
70
85
|
# Marks this record as having responded
|
71
86
|
#
|
@@ -13,8 +13,15 @@ module StubRequests
|
|
13
13
|
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
14
|
#
|
15
15
|
class Service
|
16
|
+
# includes "Comparable"
|
17
|
+
# @!parse include Comparable
|
16
18
|
include Comparable
|
17
|
-
|
19
|
+
# includes "Concerns::Property"
|
20
|
+
# @!parse include Concerns::Property
|
21
|
+
include Concerns::Property
|
22
|
+
# includes "Concerns::RegisterVerb"
|
23
|
+
# @!parse include Concerns::RegisterVerb
|
24
|
+
include Concerns::RegisterVerb
|
18
25
|
|
19
26
|
# @!attribute [rw] id
|
20
27
|
# @return [Symbol] the id of the service
|
@@ -24,10 +31,6 @@ module StubRequests
|
|
24
31
|
# @return [String] the base uri to the service
|
25
32
|
property :uri, type: String
|
26
33
|
|
27
|
-
# @!attribute [rw] endpoints
|
28
|
-
# @return [Endpoints] a list with defined endpoints
|
29
|
-
attr_reader :endpoints
|
30
|
-
|
31
34
|
#
|
32
35
|
# Initializes a new instance of a Service
|
33
36
|
#
|
@@ -37,7 +40,26 @@ module StubRequests
|
|
37
40
|
def initialize(service_id, service_uri)
|
38
41
|
self.id = service_id
|
39
42
|
self.uri = service_uri
|
40
|
-
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Register and endpoint for this service
|
47
|
+
#
|
48
|
+
# @param [Symbol] endpoint_id the id of the endpoint
|
49
|
+
# @param [Symbol] verb the HTTP verb/method
|
50
|
+
# @param [String] path the path to the endpoint
|
51
|
+
#
|
52
|
+
# @return [Endpoint] the endpoint that was registered
|
53
|
+
#
|
54
|
+
def register(endpoint_id, verb, path)
|
55
|
+
endpoint = Endpoint.new(
|
56
|
+
service_id: id,
|
57
|
+
service_uri: uri,
|
58
|
+
endpoint_id: endpoint_id,
|
59
|
+
verb: verb,
|
60
|
+
path: path,
|
61
|
+
)
|
62
|
+
EndpointRegistry.instance.register(endpoint)
|
41
63
|
end
|
42
64
|
|
43
65
|
#
|
@@ -49,6 +71,16 @@ module StubRequests
|
|
49
71
|
endpoints.any?
|
50
72
|
end
|
51
73
|
|
74
|
+
#
|
75
|
+
# The endpoints for this service
|
76
|
+
#
|
77
|
+
#
|
78
|
+
# @return [Array<Endpoints>]
|
79
|
+
#
|
80
|
+
def endpoints
|
81
|
+
EndpointRegistry[id]
|
82
|
+
end
|
83
|
+
|
52
84
|
#
|
53
85
|
# Returns a nicely formatted string with this service
|
54
86
|
#
|
@@ -59,7 +91,7 @@ module StubRequests
|
|
59
91
|
+"#<#{self.class}",
|
60
92
|
+" id=#{id}",
|
61
93
|
+" uri=#{uri}",
|
62
|
-
+" endpoints=#{
|
94
|
+
+" endpoints=#{endpoints_string}",
|
63
95
|
+">",
|
64
96
|
].join("")
|
65
97
|
end
|
@@ -73,5 +105,21 @@ module StubRequests
|
|
73
105
|
end
|
74
106
|
|
75
107
|
alias eql? ==
|
108
|
+
|
109
|
+
#
|
110
|
+
# Returns a nicely formatted string with an array of endpoints
|
111
|
+
#
|
112
|
+
#
|
113
|
+
# @return [String]
|
114
|
+
#
|
115
|
+
def endpoints_string
|
116
|
+
"[#{endpoints_as_string}]"
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def endpoints_as_string
|
122
|
+
endpoints.map(&:to_s).join(",") if endpoints?
|
123
|
+
end
|
76
124
|
end
|
77
125
|
end
|