grape-idempotency 0.1.0 β†’ 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e458d533c1b108959144d42615dcfbd9b85f741811a3150e0fd64a103469a734
4
- data.tar.gz: 55a8de15e2332ff805edd7992dab11ff4bb14576b980ee71e7f699d83fcc6f11
3
+ metadata.gz: 3141f3c611358f6110a6176beea8857d316b4f2ba859b630385f3287ba68811f
4
+ data.tar.gz: c367aa96495a47ac03243a31c79b0de163ccd2411cdcc7a673663b310083bfe5
5
5
  SHA512:
6
- metadata.gz: 720d6987f1007f27656a769ef7cc945d04fbbe2355dac226a2dd38c801d935f674ab11c80409a6c850817ce8f641ecf2fc9d3ed5b7dc6fbe66ee61e664843ded
7
- data.tar.gz: a4fe4ca95116844195429b7d54908aee357c59076095749420cce607dd87da005039e1096a436d85b82e7eaa58944c158910a41ec44780238915bbd22b9ab9cd
6
+ metadata.gz: c42121192159af6be351e82e2da316b549d1a7be17203ec146af5809dcc10df4ca058728b78de916913aaa02d8689317a69f3c365fa67ddd83cc06ebab5c7f29
7
+ data.tar.gz: 305b32f988ecac6a209019fffd42a95b34be5f938d9eff4c749e74acc48c95ad51c8c1439b26d80fd0938d7b6854f82fe53e29d43f283b40f29d83d8c41e51ff
data/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ 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.1] - 2023-01-06
8
+
9
+ ### Fix
10
+
11
+ - Return `409 - Conflict` response if idempotency key is provided for same query and body parameters BUT different endpoints.
12
+ - Use `nx: true` when storing the original request in the Redis storage for only setting the key if it does not already exist.
13
+
14
+ ### Changed
15
+
16
+ - Include `idempotency-key` in the response headers
17
+ - In the case of a concurrency error when storing the request into the redis storage (because now `nx: true`), a new idempotency key will be generated, so the consumer can check the new one seeing the headers.
18
+
7
19
  ## [0.1.0] - 2023-01-03
8
20
 
9
21
  - Initial version
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # grape-idempotency πŸ‡πŸ”
1
+ # Grape::Idempotency πŸ‡πŸ”
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/grape-idempotency.svg)](https://badge.fury.io/rb/grape-idempotency)
4
+ [![Build Status](https://github.com/jcagarcia/grape-idempotency/actions/workflows/ruby.yml/badge.svg?branch=main)](https://github.com/jcagarcia/grape-idempotency/actions)
4
5
 
5
6
  Gem for supporting idempotency in your [Grape](https://github.com/ruby-grape/grape) APIs.
6
7
 
7
-
8
8
  Topics covered in this README:
9
9
 
10
10
  - [Installation](#installation-)
@@ -75,7 +75,7 @@ Keys are automatically removed from the system if they are at least 24 hours old
75
75
 
76
76
  Results are only saved if an API endpoint begins its execution. If incoming parameters fail validation or if the request conflicts with another one executing concurrently, no idempotent result is stored because no API endpoint has initiated execution. In such cases, retrying these requests is safe.
77
77
 
78
- Additionally, this gem automatically appends the `Original-Request` header to your API's response, enabling you to trace back to the initial request that generated that specific response.
78
+ Additionally, this gem automatically appends the `Original-Request` header and the `Idempotency-Key` header to your API's response, enabling you to trace back to the initial request that generated that specific response.
79
79
 
80
80
  ## Configuration πŸͺš
81
81
 
@@ -22,10 +22,10 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.required_ruby_version = '>= 2.6'
24
24
 
25
- spec.add_runtime_dependency 'grape', '>= 1'
25
+ spec.add_runtime_dependency 'grape', '~> 1'
26
26
 
27
- spec.add_development_dependency 'bundler', '>= 2.4'
28
- spec.add_development_dependency 'rspec', '~> 3.12'
27
+ spec.add_development_dependency 'bundler'
28
+ spec.add_development_dependency 'rspec'
29
29
  spec.add_development_dependency 'rack-test'
30
- spec.add_development_dependency 'mock_redis', '~> 0.38'
30
+ spec.add_development_dependency 'mock_redis'
31
31
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Grape
4
4
  module Idempotency
5
- VERSION = '0.1.0'
5
+ VERSION = '0.1.1'
6
6
  end
7
7
  end
@@ -27,12 +27,13 @@ module Grape
27
27
  return block.call unless idempotency_key
28
28
 
29
29
  cached_request = get_from_cache(idempotency_key)
30
- if cached_request && cached_request["params"] != grape.request.params
30
+ if cached_request && (cached_request["params"] != grape.request.params || cached_request["path"] != grape.request.path)
31
31
  grape.status 409
32
32
  return configuration.conflict_error_response.to_json
33
33
  elsif cached_request
34
34
  grape.status cached_request["status"]
35
35
  grape.header(ORIGINAL_REQUEST_HEADER, cached_request["original_request"])
36
+ grape.header(configuration.idempotency_key_header, idempotency_key)
36
37
  return cached_request["response"]
37
38
  end
38
39
 
@@ -49,7 +50,10 @@ module Grape
49
50
  response
50
51
  ensure
51
52
  validate_config!
52
- store_in_cache(idempotency_key, grape.request.params, grape.status, original_request_id, response) unless cached_request
53
+ unless cached_request
54
+ stored_key = store_in_cache(idempotency_key, grape.request.path, grape.request.params, grape.status, original_request_id, response)
55
+ grape.header(configuration.idempotency_key_header, stored_key)
56
+ end
53
57
  end
54
58
 
55
59
  private
@@ -85,20 +89,38 @@ module Grape
85
89
  JSON.parse(value)
86
90
  end
87
91
 
88
- def store_in_cache(idempotency_key, params, status, request_id, response)
92
+ def store_in_cache(idempotency_key, path, params, status, request_id, response)
89
93
  body = {
94
+ path: path,
90
95
  params: params,
91
96
  status: status,
92
97
  original_request: request_id,
93
98
  response: response
94
99
  }.to_json
95
- storage.set(key(idempotency_key), body, ex: configuration.expires_in)
100
+
101
+ result = storage.set(key(idempotency_key), body, ex: configuration.expires_in, nx: true)
102
+
103
+ if !result
104
+ return store_in_cache(random_idempotency_key, path, params, status, request_id, response)
105
+ else
106
+ return idempotency_key
107
+ end
96
108
  end
97
109
 
98
110
  def key(idempotency_key)
99
111
  "grape:idempotency:#{idempotency_key}"
100
112
  end
101
113
 
114
+ def random_idempotency_key
115
+ tentative_key = SecureRandom.uuid
116
+ already_existing_key = storage.get(key(tentative_key))
117
+ if already_existing_key
118
+ return random_idempotency_key
119
+ else
120
+ return tentative_key
121
+ end
122
+ end
123
+
102
124
  def storage
103
125
  configuration.storage
104
126
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-idempotency
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
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-03 00:00:00.000000000 Z
11
+ date: 2023-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grape
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1'
27
27
  - !ruby/object:Gem::Dependency
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '2.4'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '2.4'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.12'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.12'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rack-test
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: mock_redis
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0.38'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '0.38'
82
+ version: '0'
83
83
  description: Add idempotency support to your Grape APIs for safely retrying requests
84
84
  without accidentally performing the same operation twice. When creating or updating
85
85
  an object, use an idempotency key. Then, if a connection error occurs, you can safely