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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 22c3498448dc238453b78b0ee487f46530e2f1f5f4df6795ddf84575c8688da5
4
- data.tar.gz: 35fe8f9468138f049958da4d45bd831197f232ed80c4a3757ab18a630456ff62
3
+ metadata.gz: 35a5a46ef033ee61169a80dce4812d80ed06c0e53806dfe11f9758629b8e6e75
4
+ data.tar.gz: ec88467895ef1cd544ed846067d235dc3ddac81bed797a77f3f6530d40746fe3
5
5
  SHA512:
6
- metadata.gz: 392593ddf433ed21d096440c3519b2e146cdab79d8e33616dd5e60544e850b24cc1bd4e57747bec2f6231406d3f018679a060272575f2e01c65cf37f8dca5024
7
- data.tar.gz: da684bd4265d1907b6ccd7b42dcaf2c3aa8359854542399b8301e400942b19f1b6236dde0c7eb48acea7d994ddbb63c764ce4f229c528da8f645760b3d38fd73
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
- gem 'rspec', '~> 3.0'
10
+ group :test do
11
+ gem 'rspec', '~> 3.0'
12
+ end
11
13
 
12
- gem 'rubocop', '~> 1.21'
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.1.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
- 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/affiliation_id`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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
- TODO: Write usage instructions here
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/[USERNAME]/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/[USERNAME]/affiliation_id/blob/main/CODE_OF_CONDUCT.md).
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/[USERNAME]/affiliation_id/blob/main/CODE_OF_CONDUCT.md).
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
@@ -4,5 +4,6 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
+ overcommit --install
7
8
 
8
9
  # Do any other automated setup that you need to do here
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AffiliationId
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -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
- module AffiliationId
6
- class Error < StandardError; end
7
- # Your code goes here...
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.1.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-04 00:00:00.000000000 Z
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: