grape-idempotency 0.1.1 → 0.1.3
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/CHANGELOG.md +13 -0
- data/lib/grape/idempotency/middleware/error.rb +36 -0
- data/lib/grape/idempotency/version.rb +1 -1
- data/lib/grape/idempotency.rb +91 -11
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f9a4d3d3a6e885bfa07afd8f0d51dfbfe930853d3919934016e9173f9d418a0
|
4
|
+
data.tar.gz: 81e8817b09d72d35bb71746a3451e66aed12af38888b169abc0297bb2c3f7481
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20a9654e3b1086a7dfa77379129f7bc46d18df4eadfa85ee13e7027dd91ffc6f68b83843f0d41212695287b472ebcb6e7c9b68ac322bb46f8ddadbdb9294a6ad
|
7
|
+
data.tar.gz: 60fc291bbc9558e19b2816614f0584812e08b6846b17b68af7395d15665439cad756034ff4f64d59592f56626d4fa0d5381f8dbcfc91b91294e5d223c8d4a8da
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,19 @@ All changes to `grape-idempotency` will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## [0.1.3] - 2023-01-07
|
8
|
+
|
9
|
+
### Fix
|
10
|
+
|
11
|
+
- Second calls were returning `null` when the first response was generated inside a `rescue_from`.
|
12
|
+
- Conflict response had invalid format.
|
13
|
+
|
14
|
+
## [0.1.2] - 2023-01-06
|
15
|
+
|
16
|
+
### Fix
|
17
|
+
|
18
|
+
- Return correct original response when the endpoint returns a hash in the body
|
19
|
+
|
7
20
|
## [0.1.1] - 2023-01-06
|
8
21
|
|
9
22
|
### Fix
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
class Error < Base
|
6
|
+
def run_rescue_handler(handler, error)
|
7
|
+
if handler.instance_of?(Symbol)
|
8
|
+
raise NoMethodError, "undefined method '#{handler}'" unless respond_to?(handler)
|
9
|
+
|
10
|
+
handler = public_method(handler)
|
11
|
+
end
|
12
|
+
|
13
|
+
response = handler.arity.zero? ? instance_exec(&handler) : instance_exec(error, &handler)
|
14
|
+
|
15
|
+
if response.is_a?(Rack::Response)
|
16
|
+
update_idempotency_error_with(error, response)
|
17
|
+
response
|
18
|
+
else
|
19
|
+
run_rescue_handler(:default_rescue_handler, Grape::Exceptions::InvalidResponse.new)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def update_idempotency_error_with(error, response)
|
26
|
+
begin
|
27
|
+
body = JSON.parse(response.body.join)
|
28
|
+
rescue JSON::ParserError
|
29
|
+
body = response.body.join
|
30
|
+
end
|
31
|
+
|
32
|
+
Grape::Idempotency.update_error_with_rescue_from_result(error, response.status, body)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/grape/idempotency.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'grape'
|
2
2
|
require 'securerandom'
|
3
3
|
require 'grape/idempotency/version'
|
4
|
+
require 'grape/idempotency/middleware/error'
|
4
5
|
|
5
6
|
module Grape
|
6
7
|
module Idempotency
|
@@ -29,7 +30,7 @@ module Grape
|
|
29
30
|
cached_request = get_from_cache(idempotency_key)
|
30
31
|
if cached_request && (cached_request["params"] != grape.request.params || cached_request["path"] != grape.request.path)
|
31
32
|
grape.status 409
|
32
|
-
return configuration.conflict_error_response
|
33
|
+
return configuration.conflict_error_response
|
33
34
|
elsif cached_request
|
34
35
|
grape.status cached_request["status"]
|
35
36
|
grape.header(ORIGINAL_REQUEST_HEADER, cached_request["original_request"])
|
@@ -41,21 +42,44 @@ module Grape
|
|
41
42
|
block.call
|
42
43
|
end
|
43
44
|
|
44
|
-
if
|
45
|
-
response = response[:message].to_json
|
46
|
-
end
|
45
|
+
response = response[:message] if is_an_error?(response)
|
47
46
|
|
48
47
|
original_request_id = get_request_id(grape.request.headers)
|
49
48
|
grape.header(ORIGINAL_REQUEST_HEADER, original_request_id)
|
50
|
-
response
|
49
|
+
grape.body response
|
50
|
+
rescue => e
|
51
|
+
if !cached_request && !response
|
52
|
+
validate_config!
|
53
|
+
original_request_id = get_request_id(grape.request.headers)
|
54
|
+
stored_key = store_error_request(idempotency_key, grape.request.path, grape.request.params, grape.status, original_request_id, e)
|
55
|
+
grape.header(ORIGINAL_REQUEST_HEADER, original_request_id)
|
56
|
+
grape.header(configuration.idempotency_key_header, stored_key)
|
57
|
+
end
|
58
|
+
raise
|
51
59
|
ensure
|
52
|
-
|
53
|
-
|
54
|
-
stored_key =
|
60
|
+
if !cached_request && response
|
61
|
+
validate_config!
|
62
|
+
stored_key = store_request(idempotency_key, grape.request.path, grape.request.params, grape.status, original_request_id, response)
|
55
63
|
grape.header(configuration.idempotency_key_header, stored_key)
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
67
|
+
def update_error_with_rescue_from_result(error, status, response)
|
68
|
+
validate_config!
|
69
|
+
|
70
|
+
stored_error = get_error_request_for(error)
|
71
|
+
return unless stored_error
|
72
|
+
|
73
|
+
request_with_unmanaged_error = stored_error[:request]
|
74
|
+
idempotency_key = stored_error[:idempotency_key]
|
75
|
+
path = request_with_unmanaged_error["path"]
|
76
|
+
params = request_with_unmanaged_error["params"]
|
77
|
+
original_request_id = request_with_unmanaged_error["original_request"]
|
78
|
+
|
79
|
+
store_request(idempotency_key, path, params, status, original_request_id, response)
|
80
|
+
storage.del(stored_error[:error_key])
|
81
|
+
end
|
82
|
+
|
59
83
|
private
|
60
84
|
|
61
85
|
def validate_config!
|
@@ -89,7 +113,7 @@ module Grape
|
|
89
113
|
JSON.parse(value)
|
90
114
|
end
|
91
115
|
|
92
|
-
def
|
116
|
+
def store_request(idempotency_key, path, params, status, request_id, response)
|
93
117
|
body = {
|
94
118
|
path: path,
|
95
119
|
params: params,
|
@@ -101,14 +125,70 @@ module Grape
|
|
101
125
|
result = storage.set(key(idempotency_key), body, ex: configuration.expires_in, nx: true)
|
102
126
|
|
103
127
|
if !result
|
104
|
-
return
|
128
|
+
return store_request(random_idempotency_key, path, params, status, request_id, response)
|
129
|
+
else
|
130
|
+
return idempotency_key
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def store_error_request(idempotency_key, path, params, status, request_id, error)
|
135
|
+
body = {
|
136
|
+
path: path,
|
137
|
+
params: params,
|
138
|
+
status: status,
|
139
|
+
original_request: request_id,
|
140
|
+
error: {
|
141
|
+
class_name: error.class.to_s,
|
142
|
+
message: error.message
|
143
|
+
}
|
144
|
+
}.to_json
|
145
|
+
|
146
|
+
result = storage.set(error_key(idempotency_key), body, ex: 30, nx: true)
|
147
|
+
|
148
|
+
if !result
|
149
|
+
return store_error_request(random_idempotency_key, path, params, status, request_id, error)
|
105
150
|
else
|
106
151
|
return idempotency_key
|
107
152
|
end
|
108
153
|
end
|
109
154
|
|
155
|
+
def get_error_request_for(error)
|
156
|
+
error_keys = storage.keys("#{error_key_prefix}*")
|
157
|
+
return if error_keys.empty?
|
158
|
+
|
159
|
+
error_keys.map do |key|
|
160
|
+
request_with_error = JSON.parse(storage.get(key))
|
161
|
+
error_class_name = request_with_error["error"]["class_name"]
|
162
|
+
error_message = request_with_error["error"]["message"]
|
163
|
+
|
164
|
+
if error_class_name == error.class.to_s && error_message == error.message
|
165
|
+
{
|
166
|
+
error_key: key,
|
167
|
+
request: request_with_error,
|
168
|
+
idempotency_key: key.gsub(error_key_prefix, '')
|
169
|
+
}
|
170
|
+
end
|
171
|
+
end.first
|
172
|
+
end
|
173
|
+
|
174
|
+
def is_an_error?(response)
|
175
|
+
response.is_a?(Hash) && response.has_key?(:message) && response.has_key?(:headers) && response.has_key?(:status)
|
176
|
+
end
|
177
|
+
|
110
178
|
def key(idempotency_key)
|
111
|
-
"
|
179
|
+
"#{gem_prefix}#{idempotency_key}"
|
180
|
+
end
|
181
|
+
|
182
|
+
def error_key(idempotency_key)
|
183
|
+
"#{error_key_prefix}#{idempotency_key}"
|
184
|
+
end
|
185
|
+
|
186
|
+
def error_key_prefix
|
187
|
+
"#{gem_prefix}error:"
|
188
|
+
end
|
189
|
+
|
190
|
+
def gem_prefix
|
191
|
+
"grape:idempotency:"
|
112
192
|
end
|
113
193
|
|
114
194
|
def random_idempotency_key
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape-idempotency
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Carlos García
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grape
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- lib/grape-idempotency.rb
|
99
99
|
- lib/grape/idempotency.rb
|
100
100
|
- lib/grape/idempotency/helpers.rb
|
101
|
+
- lib/grape/idempotency/middleware/error.rb
|
101
102
|
- lib/grape/idempotency/version.rb
|
102
103
|
homepage: https://github.com/jcagarcia/grape-idempotency
|
103
104
|
licenses:
|