rack-idempotency 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3a968ee6548a34d9c9847ddb7da52a4f52fd5af8
4
- data.tar.gz: c642eca5a95a7fc9a8a589545cea7c43ca71a0c9
3
+ metadata.gz: d0f2c986509883647740c552f61dac9f5c7bca1b
4
+ data.tar.gz: ffd45d68ce0a2cab353ebfc69e5c72471d136bd8
5
5
  SHA512:
6
- metadata.gz: ed16a28b9147de63b309108ca463b52dd5f22d82699f0336e125ca3e116aafed55a8350b0d9ca7c35a894597e9ad2dd9b46ac1ee3e4f4009ffaacfc29e1d0045
7
- data.tar.gz: 48e5cd558e95a13c46a8718a267ebf1bcad363a2433a0fad89dc0306115ef0bdd18365249e142655be37497c96023365a59d677be71ca116adbd8094987b0ab6
6
+ metadata.gz: 2ba38597029bd2dcccb581da0e140aee7306ba0acf09fe17cb14a68cf613af50114fdf97d3d953e3523f4c0475f52804a26b8d458cf79b879d7690bb64749fb1
7
+ data.tar.gz: bc4879106473268af4ca39be56bcb5df5205f472cc4f1b20962d9b1ddad2f87d462ac8e54b1c7f70ef2c8f0d439c1c2af575f59cde95156016a0c55c5f479da8
data/.travis.yml CHANGED
@@ -3,3 +3,8 @@ language: ruby
3
3
  rvm:
4
4
  - 2.4.0
5
5
  before_install: gem install bundler -v 1.14.0
6
+ addons:
7
+ code_climate:
8
+ repo_token: 20db8d8f86efbab70a903a66606cb5ab8e1aaaae2d76337c4fea3271ae2b2d37
9
+ after_success:
10
+ - bundle exec codeclimate-test-reporter
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
1
  # Rack::Idempotency
2
+ [![Gem Version](https://badge.fury.io/rb/rack-idempotency.svg)](https://badge.fury.io/rb/rack-idempotency)
3
+ [![Build Status](https://travis-ci.org/guitsaru/rack-idempotency.svg?branch=master)](https://travis-ci.org/guitsaru/rack-idempotency)
4
+ [![Code Climate](https://codeclimate.com/github/guitsaru/rack-idempotency/badges/gpa.svg)](https://codeclimate.com/github/guitsaru/rack-idempotency)
5
+ [![Test Coverage](https://codeclimate.com/github/guitsaru/rack-idempotency/badges/coverage.svg)](https://codeclimate.com/github/guitsaru/rack-idempotency/coverage)
2
6
 
3
7
  Rack middleware ensuring at most once requests for mutating endpoints.
4
8
 
@@ -51,7 +55,7 @@ Rack::Idempotency should handle the following cases:
51
55
  - [x] The initial connection to the server fails.
52
56
  - [ ] The request fails halfway through, leaving data in limbo.
53
57
  - [x] The request succeeds, but the connection to the client is lost.
54
-
58
+
55
59
  The second case is much more dependent on implementation. Rack::Idempotency assumes that the request is in a transaction and
56
60
  can be safely retried if it wasn't successful.
57
61
 
@@ -69,4 +73,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/guitsa
69
73
  ## License
70
74
 
71
75
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
72
-
@@ -2,13 +2,9 @@ module Rack
2
2
  class Idempotency
3
3
  # Basic version of the store. This class doesn't read or write.
4
4
  class NullStore
5
- def read(_id)
6
- nil
7
- end
5
+ def read(_id); end
8
6
 
9
- def write(_id, _value)
10
- nil
11
- end
7
+ def write(_id, _value); end
12
8
  end
13
9
  end
14
10
  end
@@ -0,0 +1,11 @@
1
+ require "rack/request"
2
+
3
+ module Rack
4
+ class Idempotency
5
+ class Request < Rack::Request
6
+ def idempotency_key
7
+ get_header("HTTP_IDEMPOTENCY_KEY")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ module Rack
2
+ class Idempotency
3
+ class RequestStorage
4
+ def initialize(store, request)
5
+ @store = store
6
+ @request = request
7
+ end
8
+
9
+ def read
10
+ stored = store.read(storage_key)
11
+ JSON.parse(stored) if stored
12
+ end
13
+
14
+ def write(response)
15
+ store.write(storage_key, response.to_json)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :request
21
+ attr_reader :store
22
+
23
+ def storage_key
24
+ "rack:idempotency:" + request.idempotency_key
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require "rack/utils"
2
+
3
+ module Rack
4
+ class Idempotency
5
+ class Response
6
+ attr_reader :status, :headers, :body
7
+
8
+ def initialize(status, headers, body)
9
+ @status = status.to_i
10
+ @headers = Rack::Utils::HeaderHash.new(headers)
11
+ @body = body
12
+ end
13
+
14
+ def success?
15
+ status.to_i >= 200 && status.to_i < 400
16
+ end
17
+
18
+ def to_a
19
+ [status, headers.to_hash, body.each]
20
+ end
21
+
22
+ def to_json
23
+ to_a.to_json
24
+ end
25
+
26
+ def idempotency_key
27
+ headers["Idempotency-Key"]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Idempotency
3
- VERSION = "0.2.0".freeze
3
+ VERSION = "0.3.0".freeze
4
4
  end
5
5
  end
@@ -4,6 +4,9 @@ require "rack/idempotency/version"
4
4
 
5
5
  require "rack/idempotency/memory_store"
6
6
  require "rack/idempotency/null_store"
7
+ require "rack/idempotency/request"
8
+ require "rack/idempotency/request_storage"
9
+ require "rack/idempotency/response"
7
10
 
8
11
  module Rack
9
12
  # Rack middleware for ensuring mutating endpoints are called at most once.
@@ -18,41 +21,33 @@ module Rack
18
21
  end
19
22
 
20
23
  def call(env)
21
- idempotency_key = env["HTTP_IDEMPOTENCY_KEY"]
22
- storage_key = generate_storage_key(idempotency_key)
23
- stored = store.read(storage_key)
24
- return JSON.parse(stored) if stored
24
+ @env = env
25
+ @request = Request.new(env.dup.freeze)
25
26
 
26
- response = @app.call(env)
27
- store_response(storage_key, response)
28
-
29
- response
27
+ lookup || store_response
30
28
  end
31
29
 
32
30
  private
33
31
 
34
- attr_reader :app, :store
35
-
36
- def store_response(storage_key, response)
37
- if successful?(response)
38
- marshaled_response = response.to_json
39
- store.write(storage_key, marshaled_response)
40
- end
32
+ attr_reader :app
33
+ attr_reader :env
34
+ attr_reader :request
35
+ attr_reader :store
41
36
 
42
- response
37
+ def request_store
38
+ @request_store ||= RequestStorage.new(store, request)
43
39
  end
44
40
 
45
- def successful?(response)
46
- status = response.first
47
-
48
- # Treat redirects as a successful response.
49
- status.to_i >= 200 && status.to_i < 400
41
+ def lookup
42
+ request_store.read
50
43
  end
51
44
 
52
- def generate_storage_key(idempotency_key)
53
- namespace = "rack:idempotency"
45
+ def store_response
46
+ response = Response.new(*@app.call(env))
47
+
48
+ request_store.write(response) if response.success?
54
49
 
55
- [namespace, idempotency_key.to_s].join(":")
50
+ response.to_a
56
51
  end
57
52
  end
58
53
  end
@@ -25,4 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "bundler", "~> 1.14"
26
26
  spec.add_development_dependency "rake", "~> 10.0"
27
27
  spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "simplecov"
29
+ spec.add_development_dependency "codeclimate-test-reporter"
28
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-idempotency
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Pruitt
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  description:
70
98
  email:
71
99
  - guitsaru@gmail.com
@@ -86,6 +114,9 @@ files:
86
114
  - lib/rack/idempotency.rb
87
115
  - lib/rack/idempotency/memory_store.rb
88
116
  - lib/rack/idempotency/null_store.rb
117
+ - lib/rack/idempotency/request.rb
118
+ - lib/rack/idempotency/request_storage.rb
119
+ - lib/rack/idempotency/response.rb
89
120
  - lib/rack/idempotency/version.rb
90
121
  - rack-idempotency.gemspec
91
122
  homepage: https://github.com/guitsaru/rack-idempotency