affiliation_id 0.1.0 → 0.2.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 +4 -4
- data/.overcommit.yml +31 -0
- data/.rubocop.yml +10 -0
- data/Gemfile +8 -2
- data/Gemfile.lock +53 -1
- data/Guardfile +44 -0
- data/README.md +136 -5
- data/affiliation_id.gemspec +36 -0
- data/bin/setup +1 -0
- data/lib/affiliation_id/configuration.rb +13 -0
- data/lib/affiliation_id/middleware/faraday.rb +36 -0
- data/lib/affiliation_id/middleware/rack.rb +36 -0
- data/lib/affiliation_id/middleware/rails.rb +19 -0
- data/lib/affiliation_id/middleware/sidekiq_client.rb +41 -0
- data/lib/affiliation_id/middleware/sidekiq_server.rb +36 -0
- data/lib/affiliation_id/railtie.rb +15 -0
- data/lib/affiliation_id/version.rb +1 -1
- data/lib/affiliation_id.rb +63 -3
- metadata +68 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35a5a46ef033ee61169a80dce4812d80ed06c0e53806dfe11f9758629b8e6e75
|
|
4
|
+
data.tar.gz: ec88467895ef1cd544ed846067d235dc3ddac81bed797a77f3f6530d40746fe3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ed858f2409de3db4083879c2422d836b15d19b51aa4c365b17cf64d5cf51f414a2cb1e0cf0b063d01f50ad648a93cbfcb61859a783aa8b018ac98f44a23b7db
|
|
7
|
+
data.tar.gz: '09830569408ffc91ba83cfd7e35fb9d0847d6f5c60db0bbe3833a866652c2e32d1c927f4b5f80c2f1494a6dc3a7cf6b8a23367751bd34ed5f9af7c9c94b6afe2'
|
data/.overcommit.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Use this file to configure the Overcommit hooks you wish to use. This will
|
|
2
|
+
# extend the default configuration defined in:
|
|
3
|
+
# https://github.com/sds/overcommit/blob/master/config/default.yml
|
|
4
|
+
#
|
|
5
|
+
# At the topmost level of this YAML file is a key representing type of hook
|
|
6
|
+
# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
|
|
7
|
+
# customize each hook, such as whether to only run it on certain files (via
|
|
8
|
+
# `include`), whether to only display output if it fails (via `quiet`), etc.
|
|
9
|
+
#
|
|
10
|
+
# For a complete list of hooks, see:
|
|
11
|
+
# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook
|
|
12
|
+
#
|
|
13
|
+
# For a complete list of options that you can use to customize hooks, see:
|
|
14
|
+
# https://github.com/sds/overcommit#configuration
|
|
15
|
+
#
|
|
16
|
+
# Uncomment the following lines to make the configuration take effect.
|
|
17
|
+
|
|
18
|
+
PreCommit:
|
|
19
|
+
RuboCop:
|
|
20
|
+
enabled: true
|
|
21
|
+
on_warn: fail # Treat all warnings as failures
|
|
22
|
+
|
|
23
|
+
TrailingWhitespace:
|
|
24
|
+
enabled: true
|
|
25
|
+
|
|
26
|
+
#PostCheckout:
|
|
27
|
+
# ALL: # Special hook name that customizes all hooks of this type
|
|
28
|
+
# quiet: true # Change all post-checkout hooks to only display output on failure
|
|
29
|
+
#
|
|
30
|
+
# IndexTags:
|
|
31
|
+
# enabled: true # Generate a tags file with `ctags` each time HEAD changes
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
TargetRubyVersion: 2.7
|
|
3
|
+
NewCops: enable
|
|
3
4
|
|
|
4
5
|
Layout/LineLength:
|
|
5
6
|
Max: 120
|
|
7
|
+
|
|
8
|
+
# ====================================================
|
|
9
|
+
# Metrics Cops
|
|
10
|
+
# ====================================================
|
|
11
|
+
Metrics/BlockLength:
|
|
12
|
+
Enabled: true
|
|
13
|
+
Exclude:
|
|
14
|
+
- spec/**/*
|
|
15
|
+
- Guardfile
|
data/Gemfile
CHANGED
|
@@ -7,6 +7,12 @@ gemspec
|
|
|
7
7
|
|
|
8
8
|
gem 'rake', '~> 13.0'
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
group :test do
|
|
11
|
+
gem 'rspec', '~> 3.0'
|
|
12
|
+
end
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
group :development, :test do
|
|
15
|
+
gem 'guard-rspec', '~> 4.7', '>= 4.7.3', require: false
|
|
16
|
+
gem 'overcommit', '~> 0.59.1', require: false
|
|
17
|
+
gem 'rubocop', '~> 1.21'
|
|
18
|
+
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,65 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
affiliation_id (0.
|
|
4
|
+
affiliation_id (0.2.0)
|
|
5
|
+
faraday (>= 0.17.0, < 3)
|
|
6
|
+
rack (>= 1.4)
|
|
5
7
|
|
|
6
8
|
GEM
|
|
7
9
|
remote: https://rubygems.org/
|
|
8
10
|
specs:
|
|
9
11
|
ast (2.4.2)
|
|
12
|
+
byebug (11.1.3)
|
|
13
|
+
childprocess (4.1.0)
|
|
14
|
+
coderay (1.1.3)
|
|
10
15
|
diff-lcs (1.5.0)
|
|
16
|
+
faraday (2.5.2)
|
|
17
|
+
faraday-net_http (>= 2.0, < 3.1)
|
|
18
|
+
ruby2_keywords (>= 0.0.4)
|
|
19
|
+
faraday-net_http (3.0.0)
|
|
20
|
+
ffi (1.15.5)
|
|
21
|
+
formatador (1.1.0)
|
|
22
|
+
guard (2.18.0)
|
|
23
|
+
formatador (>= 0.2.4)
|
|
24
|
+
listen (>= 2.7, < 4.0)
|
|
25
|
+
lumberjack (>= 1.0.12, < 2.0)
|
|
26
|
+
nenv (~> 0.1)
|
|
27
|
+
notiffany (~> 0.0)
|
|
28
|
+
pry (>= 0.13.0)
|
|
29
|
+
shellany (~> 0.0)
|
|
30
|
+
thor (>= 0.18.1)
|
|
31
|
+
guard-compat (1.2.1)
|
|
32
|
+
guard-rspec (4.7.3)
|
|
33
|
+
guard (~> 2.1)
|
|
34
|
+
guard-compat (~> 1.1)
|
|
35
|
+
rspec (>= 2.99.0, < 4.0)
|
|
36
|
+
iniparse (1.5.0)
|
|
11
37
|
json (2.6.2)
|
|
38
|
+
listen (3.7.1)
|
|
39
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
40
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
|
41
|
+
lumberjack (1.2.8)
|
|
42
|
+
method_source (1.0.0)
|
|
43
|
+
nenv (0.3.0)
|
|
44
|
+
notiffany (0.1.3)
|
|
45
|
+
nenv (~> 0.1)
|
|
46
|
+
shellany (~> 0.0)
|
|
47
|
+
overcommit (0.59.1)
|
|
48
|
+
childprocess (>= 0.6.3, < 5)
|
|
49
|
+
iniparse (~> 1.4)
|
|
50
|
+
rexml (~> 3.2)
|
|
12
51
|
parallel (1.22.1)
|
|
13
52
|
parser (3.1.2.0)
|
|
14
53
|
ast (~> 2.4.1)
|
|
54
|
+
pry (0.14.1)
|
|
55
|
+
coderay (~> 1.1)
|
|
56
|
+
method_source (~> 1.0)
|
|
57
|
+
rack (2.2.4)
|
|
15
58
|
rainbow (3.1.1)
|
|
16
59
|
rake (13.0.6)
|
|
60
|
+
rb-fsevent (0.11.1)
|
|
61
|
+
rb-inotify (0.10.1)
|
|
62
|
+
ffi (~> 1.0)
|
|
17
63
|
regexp_parser (2.5.0)
|
|
18
64
|
rexml (3.2.5)
|
|
19
65
|
rspec (3.11.0)
|
|
@@ -42,6 +88,9 @@ GEM
|
|
|
42
88
|
rubocop-ast (1.19.1)
|
|
43
89
|
parser (>= 3.1.1.0)
|
|
44
90
|
ruby-progressbar (1.11.0)
|
|
91
|
+
ruby2_keywords (0.0.5)
|
|
92
|
+
shellany (0.0.1)
|
|
93
|
+
thor (1.2.1)
|
|
45
94
|
unicode-display_width (2.2.0)
|
|
46
95
|
|
|
47
96
|
PLATFORMS
|
|
@@ -49,6 +98,9 @@ PLATFORMS
|
|
|
49
98
|
|
|
50
99
|
DEPENDENCIES
|
|
51
100
|
affiliation_id!
|
|
101
|
+
byebug (~> 11.1, >= 11.1.3)
|
|
102
|
+
guard-rspec (~> 4.7, >= 4.7.3)
|
|
103
|
+
overcommit (~> 0.59.1)
|
|
52
104
|
rake (~> 13.0)
|
|
53
105
|
rspec (~> 3.0)
|
|
54
106
|
rubocop (~> 1.21)
|
data/Guardfile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A sample Guardfile
|
|
4
|
+
# More info at https://github.com/guard/guard#readme
|
|
5
|
+
|
|
6
|
+
## Uncomment and set this to only include directories you want to watch
|
|
7
|
+
# directories %w(app lib config test spec features) \
|
|
8
|
+
# .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
|
9
|
+
|
|
10
|
+
## NOTE: if you are using the `directories` clause above and you are not
|
|
11
|
+
## watching the project directory ('.'), then you will want to move
|
|
12
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
|
13
|
+
#
|
|
14
|
+
# $ mkdir config
|
|
15
|
+
# $ mv Guardfile config/
|
|
16
|
+
# $ ln -s config/Guardfile .
|
|
17
|
+
#
|
|
18
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
|
19
|
+
|
|
20
|
+
# NOTE: The cmd option is now required due to the increasing number of ways
|
|
21
|
+
# rspec may be run, below are examples of the most common uses.
|
|
22
|
+
# * bundler: 'bundle exec rspec'
|
|
23
|
+
# * bundler binstubs: 'bin/rspec'
|
|
24
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
|
25
|
+
# installed the spring binstubs per the docs)
|
|
26
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
|
27
|
+
# * 'just' rspec: 'rspec'
|
|
28
|
+
|
|
29
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
|
30
|
+
require 'guard/rspec/dsl'
|
|
31
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
|
32
|
+
|
|
33
|
+
# Feel free to open issues for suggestions and improvements
|
|
34
|
+
|
|
35
|
+
# RSpec files
|
|
36
|
+
rspec = dsl.rspec
|
|
37
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
|
38
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
|
39
|
+
watch(rspec.spec_files)
|
|
40
|
+
|
|
41
|
+
# Ruby files
|
|
42
|
+
ruby = dsl.ruby
|
|
43
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
|
44
|
+
end
|
data/README.md
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
# AffiliationId
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AffiliationId is a middleware collection for different frameworks and gems with the purpose of making end-to-end request tracing as easy as possible.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The concept is really simple and it's meant to work like this:
|
|
6
|
+
|
|
7
|
+
1. A request that reaches a web app or API it's the entry point and should have a unique ID.
|
|
8
|
+
2. That ID is propagated throughout the app in all parts that are a consequence of the initial request, things like: requests to third-party APIs, Sidekiq jobs, error trackers, etc.
|
|
9
|
+
3. The ID is included in all the logs statements so they can be traced based on the ID
|
|
10
|
+
|
|
11
|
+
Although there are tools like [OpenTelemetry](https://opentelemetry.io/), which is great, that is a much more complex tool and brings a lot of overhead that maybe is not worth it for some applications or teams.
|
|
12
|
+
|
|
13
|
+
Currently implemented middleware:
|
|
14
|
+
|
|
15
|
+
- Rack
|
|
16
|
+
- Rails
|
|
17
|
+
- Faraday
|
|
18
|
+
- Sidekiq
|
|
6
19
|
|
|
7
20
|
## Installation
|
|
8
21
|
|
|
@@ -22,7 +35,125 @@ Or install it yourself as:
|
|
|
22
35
|
|
|
23
36
|
## Usage
|
|
24
37
|
|
|
25
|
-
|
|
38
|
+
Under the hood AffiliationId uses `SecureRandom.uuid` to generate unique ID's when needed.
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# Get current ID
|
|
42
|
+
AffiliationId.current_id
|
|
43
|
+
=> '93f971bb-b889-4223-ac57-5d39f34051a4'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`.current_id` is memoized and subsequent calls will return the same value
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
# Set ID
|
|
50
|
+
AffiliationId.current_id = 'myID'
|
|
51
|
+
|
|
52
|
+
AffiliationId.current_id
|
|
53
|
+
=> 'myID'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# Overwrite current_id with a new generated value
|
|
58
|
+
AffiliationId.current_id
|
|
59
|
+
=> '93f971bb-b889-4223-ac57-5d39f34051a4'
|
|
60
|
+
|
|
61
|
+
AffiliationId.renew_current_id!
|
|
62
|
+
=>'55e94f67-5fce-4226-98d5-4c149684debe'
|
|
63
|
+
|
|
64
|
+
AffiliationId.current_id
|
|
65
|
+
=> '55e94f67-5fce-4226-98d5-4c149684debe'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
# Reset/Clear current_id
|
|
70
|
+
|
|
71
|
+
AffiliationId.current_id
|
|
72
|
+
=> '55e94f67-5fce-4226-98d5-4c149684debe'
|
|
73
|
+
|
|
74
|
+
AffiliationId.reset!
|
|
75
|
+
=> nil
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Rack
|
|
79
|
+
|
|
80
|
+
The middleware for rack based applications is inspired from the Rails version of this middleware [ActionDispatch::RequestId](https://api.rubyonrails.org/classes/ActionDispatch/RequestId.html).
|
|
81
|
+
|
|
82
|
+
It looks at the `X-Affiliation-ID`, if the header is found and a value is present, this will be set as the `Affiliation.current_id` throughout the request, otherwise an random ID is generated.
|
|
83
|
+
|
|
84
|
+
To use the middleware, it needs to be added to the app middleware stack.
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# config.ru
|
|
88
|
+
require 'affiliation_id/middleware/rack'
|
|
89
|
+
|
|
90
|
+
use AffiliationId::Middleware::Rack
|
|
91
|
+
|
|
92
|
+
run MyApp.new
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Rails
|
|
96
|
+
|
|
97
|
+
In true Rails fashion, this works out of the box after installing the gem. Although it still depends on [ActionDispatch::RequestId](https://api.rubyonrails.org/classes/ActionDispatch/RequestId.html) which is included by default in all new Rails apps.
|
|
98
|
+
|
|
99
|
+
### Faraday
|
|
100
|
+
|
|
101
|
+
By using the Faraday middleware the, all requests will include a header `X-Affiliation-ID` with the value of `AffiliationID.current_id`.
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
require 'affiliation_id/middleware/faraday'
|
|
105
|
+
|
|
106
|
+
# ...
|
|
107
|
+
|
|
108
|
+
conn = Faraday.new do |f|
|
|
109
|
+
f.request :affiliation_id # include AffiliationID.current_id in the request headers
|
|
110
|
+
f.adapter :net_http # Use the Net::HTTP adapter
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Sidekiq
|
|
115
|
+
|
|
116
|
+
For more information on how [Sidekiq Middleware](https://github.com/mperham/sidekiq/wiki/Middleware) works, I suggest reading id directly from the Sidekiq documentation.
|
|
117
|
+
|
|
118
|
+
Configuration example:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# sidekiq_initializer.rb
|
|
122
|
+
|
|
123
|
+
require 'affiliation_id/middleware/sidekiq_client'
|
|
124
|
+
require 'affiliation_id/middleware/sidekiq_server'
|
|
125
|
+
|
|
126
|
+
Sidekiq.configure_client do |config|
|
|
127
|
+
config.client_middleware do |chain|
|
|
128
|
+
chain.add AffiliationId::Middleware::SidekiqClient
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
Sidekiq.configure_server do |config|
|
|
133
|
+
config.client_middleware do |chain|
|
|
134
|
+
chain.add AffiliationId::Middleware::SidekiqClient
|
|
135
|
+
end
|
|
136
|
+
config.server_middleware do |chain|
|
|
137
|
+
chain.add AffiliationId::Middleware::SidekiqServer
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
# affiliation_id_initializer.rb
|
|
146
|
+
|
|
147
|
+
AffiliationId.configure do |config|
|
|
148
|
+
# By default AffiliationId.current_id will raise an AffiliationId::MissingCurrentId exception if the value was not previously set.
|
|
149
|
+
# To opt in to the behavior of generating the id automatically config the following setting to false.
|
|
150
|
+
# config.enforce_explicit_current_id = false (Default: true)
|
|
151
|
+
|
|
152
|
+
# AffiliationId uses 'X-Request-ID' as the default header name.
|
|
153
|
+
# This can be changed by the following config.
|
|
154
|
+
# config.header_name = 'My-Custom-Header' (Default: 'X-Request-ID')
|
|
155
|
+
end
|
|
156
|
+
```
|
|
26
157
|
|
|
27
158
|
## Development
|
|
28
159
|
|
|
@@ -32,7 +163,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
32
163
|
|
|
33
164
|
## Contributing
|
|
34
165
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
166
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/siklodi-mariusz/affiliation_id. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/siklodi-mariusz/affiliation_id/blob/main/CODE_OF_CONDUCT.md).
|
|
36
167
|
|
|
37
168
|
## License
|
|
38
169
|
|
|
@@ -40,4 +171,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
40
171
|
|
|
41
172
|
## Code of Conduct
|
|
42
173
|
|
|
43
|
-
Everyone interacting in the AffiliationId project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
|
174
|
+
Everyone interacting in the AffiliationId project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/siklodi-mariusz/affiliation_id/blob/main/CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/affiliation_id/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'affiliation_id'
|
|
7
|
+
spec.version = AffiliationId::VERSION
|
|
8
|
+
|
|
9
|
+
spec.authors = ['Mariusz Siklodi']
|
|
10
|
+
spec.summary = 'Track requests by affiliating them with an uniq id.'
|
|
11
|
+
spec.homepage = 'https://github.com/siklodi-mariusz/affiliation_id'
|
|
12
|
+
spec.license = 'MIT'
|
|
13
|
+
spec.required_ruby_version = '>= 2.7.0'
|
|
14
|
+
|
|
15
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
16
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
17
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
18
|
+
|
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
22
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
23
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
spec.require_paths = ['lib']
|
|
27
|
+
|
|
28
|
+
# Uncomment to register a new dependency of your gem
|
|
29
|
+
spec.add_dependency 'faraday', '>= 0.17.0', '< 3'
|
|
30
|
+
spec.add_dependency 'rack', '>= 1.4'
|
|
31
|
+
|
|
32
|
+
spec.add_development_dependency 'byebug', '~> 11.1', '>= 11.1.3'
|
|
33
|
+
|
|
34
|
+
# For more information and examples about making a new gem, checkout our
|
|
35
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
|
36
|
+
end
|
data/bin/setup
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AffiliationId
|
|
4
|
+
class Configuration # :nodoc:
|
|
5
|
+
HEADER_NAME = 'X-Request-ID'
|
|
6
|
+
attr_accessor :enforce_explicit_current_id, :header_name
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@enforce_explicit_current_id = true
|
|
10
|
+
@header_name = HEADER_NAME
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
require_relative '../../affiliation_id'
|
|
6
|
+
|
|
7
|
+
module AffiliationId
|
|
8
|
+
module Middleware
|
|
9
|
+
#
|
|
10
|
+
# Faraday::Middleware for handling requests made with Faraday with affiliation_id
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# conn = Faraday.new do |f|
|
|
14
|
+
# f.request :affiliation_id # include AffiliationID.current_id in the request headers
|
|
15
|
+
# f.adapter :net_http # Use the Net::HTTP adapter
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
class Faraday < ::Faraday::Middleware
|
|
19
|
+
def call(env)
|
|
20
|
+
env[:request_headers][config.header_name] ||= AffiliationId.current_id
|
|
21
|
+
|
|
22
|
+
@app.call(env)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def config
|
|
28
|
+
AffiliationId.configuration
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if Faraday::Request.respond_to? :register_middleware
|
|
35
|
+
Faraday::Request.register_middleware affiliation_id: AffiliationId::Middleware::Faraday
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../affiliation_id'
|
|
4
|
+
|
|
5
|
+
module AffiliationId
|
|
6
|
+
module Middleware
|
|
7
|
+
#
|
|
8
|
+
# Rack middleware to add Affiliation ID HTTP header to rack based web frameworks
|
|
9
|
+
#
|
|
10
|
+
class Rack
|
|
11
|
+
def initialize(app)
|
|
12
|
+
@app = app
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(env)
|
|
16
|
+
AffiliationId.current_id = header_from_env(env) || AffiliationId.renew_current_id!
|
|
17
|
+
|
|
18
|
+
@app.call(env).tap { |_status, headers, _body| headers[config.header_name] = AffiliationId.current_id }
|
|
19
|
+
ensure
|
|
20
|
+
AffiliationId.reset!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def header_from_env(env)
|
|
26
|
+
value = env["HTTP_#{config.header_name.upcase.tr('-', '_')}"]
|
|
27
|
+
|
|
28
|
+
value&.gsub(/[^\w\-@.]/, '')&.[](0...255)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def config
|
|
32
|
+
AffiliationId.configuration
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../affiliation_id'
|
|
4
|
+
|
|
5
|
+
module AffiliationId
|
|
6
|
+
module Middleware
|
|
7
|
+
class Rails # :nodoc:
|
|
8
|
+
def initialize(app)
|
|
9
|
+
@app = app
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(env)
|
|
13
|
+
req = ActionDispatch::Request.new env
|
|
14
|
+
AffiliationId.current_id = req.request_id
|
|
15
|
+
@app.call(env)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../affiliation_id'
|
|
4
|
+
|
|
5
|
+
module AffiliationId
|
|
6
|
+
module Middleware
|
|
7
|
+
#
|
|
8
|
+
# Sidekiq client middleware to inject the AffiliationId.current_id in the Job
|
|
9
|
+
#
|
|
10
|
+
# When Sidekiq client pushes a job in queue, at minimum, the job contains the following attributes:
|
|
11
|
+
#
|
|
12
|
+
# {
|
|
13
|
+
# "class": "SomeWorker",
|
|
14
|
+
# "jid": "b4a577edbccf1d805744efa9", // 12-byte random number as 24 char hex string
|
|
15
|
+
# "args": [1, "arg", true],
|
|
16
|
+
# "created_at": 1234567890,
|
|
17
|
+
# "enqueued_at": 1234567890
|
|
18
|
+
# }
|
|
19
|
+
#
|
|
20
|
+
# This middleware adds to this list of attributes the current value of AffiliationId.current_id.
|
|
21
|
+
# So, if AffiliationId.current_id returns a value of "93f971bb-b889-4223-ac57-5d39f34051a4",
|
|
22
|
+
# the end result will look something like this:
|
|
23
|
+
#
|
|
24
|
+
# {
|
|
25
|
+
# "class": "SomeWorker",
|
|
26
|
+
# "jid": "b4a577edbccf1d805744efa9", // 12-byte random number as 24 char hex string
|
|
27
|
+
# "args": [1, "arg", true],
|
|
28
|
+
# "created_at": 1234567890,
|
|
29
|
+
# "enqueued_at": 1234567890,
|
|
30
|
+
# "affiliation_id": "93f971bb-b889-4223-ac57-5d39f34051a4"
|
|
31
|
+
# }
|
|
32
|
+
#
|
|
33
|
+
#
|
|
34
|
+
class SidekiqClient
|
|
35
|
+
def call(_, job, _, _)
|
|
36
|
+
job[::AffiliationId::SIDEKIQ_JOB_KEY] = ::AffiliationId.current_id
|
|
37
|
+
yield
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../affiliation_id'
|
|
4
|
+
|
|
5
|
+
module AffiliationId
|
|
6
|
+
module Middleware
|
|
7
|
+
#
|
|
8
|
+
# Sidekiq server middleware to set AffiliationId.current_id from the job hash
|
|
9
|
+
#
|
|
10
|
+
# Assuming AffiliationId::Middleware::SidekiqClient is used.
|
|
11
|
+
# All jobs pushed in the queue will include an affiliation_id
|
|
12
|
+
#
|
|
13
|
+
# {
|
|
14
|
+
# "class": "SomeWorker",
|
|
15
|
+
# "jid": "b4a577edbccf1d805744efa9", // 12-byte random number as 24 char hex string
|
|
16
|
+
# "args": [1, "arg", true],
|
|
17
|
+
# "created_at": 1234567890,
|
|
18
|
+
# "enqueued_at": 1234567890,
|
|
19
|
+
# "affiliation_id": "93f971bb-b889-4223-ac57-5d39f34051a4"
|
|
20
|
+
# }
|
|
21
|
+
#
|
|
22
|
+
# This middleware will take that value and set AffiliationId.current_id= to it.
|
|
23
|
+
#
|
|
24
|
+
# If there is no affiliation_id attribute in the job Hash, there is a fallback to AffiliationId.current_id,
|
|
25
|
+
# which depending on the configuration of :enforce_explicit_current_id raises an error or generates a new id.
|
|
26
|
+
#
|
|
27
|
+
class SidekiqServer
|
|
28
|
+
def call(_, job, _)
|
|
29
|
+
::AffiliationId.current_id = job[::AffiliationId::SIDEKIQ_JOB_KEY] || ::AffiliationId.current_id
|
|
30
|
+
yield
|
|
31
|
+
ensure
|
|
32
|
+
AffiliationId.reset!
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'middleware/rails'
|
|
4
|
+
|
|
5
|
+
module AffiliationId
|
|
6
|
+
class Railtie < ::Rails::Railtie # :nodoc:
|
|
7
|
+
initializer 'affiliation_id.initializer' do |app|
|
|
8
|
+
app.middleware.insert_after ActionDispatch::RequestId, AffiliationId::Middleware::Rails
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
config.to_prepare do
|
|
12
|
+
AffiliationId.reset!
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/affiliation_id.rb
CHANGED
|
@@ -1,8 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
3
5
|
require_relative 'affiliation_id/version'
|
|
6
|
+
require_relative 'affiliation_id/configuration'
|
|
7
|
+
require_relative 'affiliation_id/middleware/faraday'
|
|
8
|
+
require_relative 'affiliation_id/middleware/sidekiq_client'
|
|
9
|
+
require_relative 'affiliation_id/middleware/sidekiq_server'
|
|
10
|
+
require_relative 'affiliation_id/railtie' if defined?(Rails::Railtie)
|
|
11
|
+
|
|
12
|
+
module AffiliationId # :nodoc:
|
|
13
|
+
THREAD_KEY = 'AFFILIATION_ID'
|
|
14
|
+
SIDEKIQ_JOB_KEY = 'affiliation_id'
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
attr_writer :configuration
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
# Returns the current Affiliation ID
|
|
21
|
+
#
|
|
22
|
+
# @return [String] Uniq Affiliation ID
|
|
23
|
+
#
|
|
24
|
+
def current_id
|
|
25
|
+
raise MissingCurrentId if Thread.current[THREAD_KEY].nil? && configuration.enforce_explicit_current_id
|
|
26
|
+
|
|
27
|
+
Thread.current[THREAD_KEY] ||= SecureRandom.uuid
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Sets a new ID to be used as Affiliation ID
|
|
32
|
+
#
|
|
33
|
+
# @param [String] value of Affilication ID
|
|
34
|
+
#
|
|
35
|
+
# @return [String] Affiliation ID
|
|
36
|
+
#
|
|
37
|
+
def current_id=(value)
|
|
38
|
+
Thread.current[THREAD_KEY] = value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# Renew the current Affiliation ID with a new one
|
|
43
|
+
#
|
|
44
|
+
# @return [String] Affiliation ID
|
|
45
|
+
#
|
|
46
|
+
def renew_current_id!
|
|
47
|
+
Thread.current[THREAD_KEY] = SecureRandom.uuid
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def reset!
|
|
51
|
+
Thread.current[THREAD_KEY] = nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def configuration
|
|
55
|
+
@configuration ||= Configuration.new
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def configure
|
|
59
|
+
yield configuration
|
|
60
|
+
end
|
|
61
|
+
end
|
|
4
62
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
63
|
+
class MissingCurrentId < StandardError # :nodoc:
|
|
64
|
+
def to_s
|
|
65
|
+
'Affiliation ID must be set explicitly'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
8
68
|
end
|
metadata
CHANGED
|
@@ -1,32 +1,96 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: affiliation_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mariusz Siklodi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-08-
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2022-08-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: faraday
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.17.0
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '3'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 0.17.0
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: rack
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.4'
|
|
40
|
+
type: :runtime
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.4'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: byebug
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '11.1'
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 11.1.3
|
|
57
|
+
type: :development
|
|
58
|
+
prerelease: false
|
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - "~>"
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '11.1'
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: 11.1.3
|
|
13
67
|
description:
|
|
14
68
|
email:
|
|
15
69
|
executables: []
|
|
16
70
|
extensions: []
|
|
17
71
|
extra_rdoc_files: []
|
|
18
72
|
files:
|
|
73
|
+
- ".overcommit.yml"
|
|
19
74
|
- ".rspec"
|
|
20
75
|
- ".rubocop.yml"
|
|
21
76
|
- CODE_OF_CONDUCT.md
|
|
22
77
|
- Gemfile
|
|
23
78
|
- Gemfile.lock
|
|
79
|
+
- Guardfile
|
|
24
80
|
- LICENSE.txt
|
|
25
81
|
- README.md
|
|
26
82
|
- Rakefile
|
|
83
|
+
- affiliation_id.gemspec
|
|
27
84
|
- bin/console
|
|
28
85
|
- bin/setup
|
|
29
86
|
- lib/affiliation_id.rb
|
|
87
|
+
- lib/affiliation_id/configuration.rb
|
|
88
|
+
- lib/affiliation_id/middleware/faraday.rb
|
|
89
|
+
- lib/affiliation_id/middleware/rack.rb
|
|
90
|
+
- lib/affiliation_id/middleware/rails.rb
|
|
91
|
+
- lib/affiliation_id/middleware/sidekiq_client.rb
|
|
92
|
+
- lib/affiliation_id/middleware/sidekiq_server.rb
|
|
93
|
+
- lib/affiliation_id/railtie.rb
|
|
30
94
|
- lib/affiliation_id/version.rb
|
|
31
95
|
homepage: https://github.com/siklodi-mariusz/affiliation_id
|
|
32
96
|
licenses:
|
|
@@ -34,6 +98,7 @@ licenses:
|
|
|
34
98
|
metadata:
|
|
35
99
|
homepage_uri: https://github.com/siklodi-mariusz/affiliation_id
|
|
36
100
|
source_code_uri: https://github.com/siklodi-mariusz/affiliation_id
|
|
101
|
+
rubygems_mfa_required: 'true'
|
|
37
102
|
post_install_message:
|
|
38
103
|
rdoc_options: []
|
|
39
104
|
require_paths:
|