mirror of
https://github.com/ditkrg/idempotent-request.git
synced 2026-01-22 22:06:44 +00:00
update readme
This commit is contained in:
parent
b82e271caa
commit
2d5542f256
116
README.md
116
README.md
@ -1,8 +1,6 @@
|
|||||||
# Idempotent::Request
|
# Idempotent Request
|
||||||
|
|
||||||
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/idempotent/request`. To experiment with that code, run `bin/console` for an interactive prompt.
|
Rack middleware ensuring at most once requests for mutating endpoints.
|
||||||
|
|
||||||
TODO: Delete this and the text above, and describe your gem
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -20,15 +18,115 @@ Or install it yourself as:
|
|||||||
|
|
||||||
$ gem install idempotent-request
|
$ gem install idempotent-request
|
||||||
|
|
||||||
## Usage
|
## How it works
|
||||||
|
|
||||||
TODO: Write usage instructions here
|
1. Front-end generates a unique `key` then a user goes to a specific route (for example, transfer page).
|
||||||
|
2. When user clicks "Submit" button, the `key` is sent in the header `idempotency-key` and back-end stores server response into redis.
|
||||||
|
3. All the consecutive requests with the `key` won't be executer by the server and the result of previous response (2) will be fetched from redis.
|
||||||
|
4. Once the user leaves or refreshes the page, front-end should re-generate the key.
|
||||||
|
|
||||||
## Development
|
## Configuration
|
||||||
|
```ruby
|
||||||
|
# application.rb
|
||||||
|
config.middleware.use IdempotentRequest::Middleware,
|
||||||
|
storage: IdempotentRequest::RedisStorage.new(::Redis.current, expire_time: 1.day),
|
||||||
|
policy: YOUR_CLASS
|
||||||
|
```
|
||||||
|
|
||||||
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
To define a policy, whether a request should be idempotent, you have to provider a class with the following interface:
|
||||||
|
|
||||||
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
```ruby
|
||||||
|
class Policy
|
||||||
|
attr_reader :request
|
||||||
|
|
||||||
|
def initialize(request)
|
||||||
|
@request = request
|
||||||
|
end
|
||||||
|
|
||||||
|
def should?
|
||||||
|
# request is Rack::Request class
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example of integration for rails
|
||||||
|
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# application.rb
|
||||||
|
config.middleware.use IdempotentRequest::Middleware,
|
||||||
|
storage: IdempotentRequest::RedisStorage.new(::Redis.current, expire_time: 1.day),
|
||||||
|
policy: IdempotentRequest::Policy
|
||||||
|
|
||||||
|
config.idempotent_routes = [
|
||||||
|
{ controller: :'v1/transfers', action: :create },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# lib/idempotent-request/policy.rb
|
||||||
|
module IdempotentRequest
|
||||||
|
class Policy
|
||||||
|
attr_reader :request
|
||||||
|
|
||||||
|
def initialize(request)
|
||||||
|
@request = request
|
||||||
|
end
|
||||||
|
|
||||||
|
def should?
|
||||||
|
route = Rails.application.routes.recognize_path(request.path, method: request.request_method)
|
||||||
|
Rails.application.config.idempotent_routes.any? do |idempotent_route|
|
||||||
|
idempotent_route[:controller] == route[:controller].to_sym &&
|
||||||
|
idempotent_route[:action] == route[:action].to_sym
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom options
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# application.rb
|
||||||
|
config.middleware.use IdempotentRequest::Middleware,
|
||||||
|
header_key: 'X-Qonto-Idempotency-Key', # by default Idempotency-key
|
||||||
|
policy: IdempotentRequest::Policy,
|
||||||
|
callback: IdempotentRequest::RailsCallback,
|
||||||
|
storage: IdempotentRequest::RedisStorage.new(::Redis.current, expire_time: 1.day, namespace: 'idempotency_keys')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Policy
|
||||||
|
|
||||||
|
Custom class to decide whether the request should be idempotent.
|
||||||
|
|
||||||
|
See *Example of integration for rails*
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
Where the response will be stored. Can be any class that implements the following interface:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def read(key)
|
||||||
|
# read from a storage
|
||||||
|
end
|
||||||
|
|
||||||
|
def write(key, payload)
|
||||||
|
# write to a storage
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Callback
|
||||||
|
|
||||||
|
Get notified when the client sends a request with the same idempotency key:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class RailsCallback < Callback
|
||||||
|
def detected(key:)
|
||||||
|
# `request` is also available
|
||||||
|
Rails.logger.warn "IdempotentRequest request detected, key: #{key}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,8 @@ Gem::Specification.new do |spec|
|
|||||||
spec.authors = ['Dmytro Zakharov']
|
spec.authors = ['Dmytro Zakharov']
|
||||||
spec.email = ['dmytro@qonto.eu']
|
spec.email = ['dmytro@qonto.eu']
|
||||||
|
|
||||||
spec.summary = %q{Write a short summary, because Rubygems requires one.}
|
spec.summary = %q{Rack middleware ensuring at most once requests for mutating endpoints.}
|
||||||
spec.description = %q{Write a longer description or delete this line.}
|
spec.homepage = 'https://github.com/qonto/idempotent-request'
|
||||||
spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
|
||||||
spec.license = 'MIT'
|
spec.license = 'MIT'
|
||||||
|
|
||||||
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user