stitches 3.8.1 → 4.0.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: ac1389298b9ccff356bb73c3e925501265eddb4d68932ac7bc6e41ab35535353
4
- data.tar.gz: c800e4a091a67181e065cb0d42c45a7a730b710ce4a83fb513b6606ea7e6e47d
3
+ metadata.gz: 1224843bf19c711e4058e54025f2507fe4dd0085fb5afdd57d5d383bf8d1b03e
4
+ data.tar.gz: 8f2c1f7f053d711e6419b8e19c23cc123ce7164513b7cb3978091d081612001a
5
5
  SHA512:
6
- metadata.gz: 439faf8691f51448dad6b45b35c77a63e5b31075181a55e6fe03ec1381bfe91841cc2a73172390d6e32b5f1f38f7fd3ea069b15c98d8086f4fd21b1c0842b241
7
- data.tar.gz: 8c435dc519da97c5cc3aec267dffcb0ffb96e2479b98c14ac44217b5dd4de50afdd818c18063f7d126e7cb56a97f197bd592235591fe9e982243f12314b633d0
6
+ metadata.gz: f16b168bf89994f06816661fb0dda066c248a3c55a22bdeebebf32d4a0e784368b41a460f25a84c9e0c4a694b37d372a24cf739171191925f31df7df7a562be6
7
+ data.tar.gz: 053d0ae0e44945437218bc87206038029065b0ff8b6f8321bbad8b878024bddb16432b0f23136286e6019805a716d541a37a2d5382479b9216a9cde0104bf5d9
@@ -3,9 +3,33 @@
3
3
  ---
4
4
  version: 2
5
5
  jobs:
6
+ generate-and-push-docs:
7
+ docker:
8
+ - image: circleci/ruby:2.7.2
9
+ auth:
10
+ username: "$DOCKERHUB_USERNAME"
11
+ password: "$DOCKERHUB_PASSWORD"
12
+ steps:
13
+ - checkout
14
+ - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
15
+ - run: bundle install --full-index
16
+ - run:
17
+ name: Generate documentation
18
+ command: ' if [[ $(bundle exec rake -T docs:generate:custom) ]]; then echo
19
+ "Generating docs using rake task docs:generate:custom" ; bundle exec rake
20
+ docs:generate:custom ; elif [[ $(bundle exec rake -T docs:generate) ]];
21
+ then echo "Generating docs using rake task docs:generate" ; bundle exec
22
+ rake docs:generate ; else echo "Skipping doc generation" ; exit 0 ; fi '
23
+ - run:
24
+ name: Push documentation to Unwritten
25
+ command: if [[ $(bundle exec rake -T docs:push) ]]; then bundle exec rake
26
+ docs:push; fi
6
27
  release:
7
28
  docker:
8
- - image: circleci/ruby:2.6.4
29
+ - image: circleci/ruby:2.7.2
30
+ auth:
31
+ username: "$DOCKERHUB_USERNAME"
32
+ password: "$DOCKERHUB_PASSWORD"
9
33
  steps:
10
34
  - checkout
11
35
  - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
@@ -17,14 +41,22 @@ jobs:
17
41
  - run:
18
42
  name: Build/release gem to artifactory
19
43
  command: bundle exec rake push_artifactory
20
- ruby-2.6.4-rails-6.0:
44
+ ruby-2.7.2-rails-6.0:
21
45
  docker:
22
- - image: circleci/ruby:2.6.4
46
+ - image: circleci/ruby:2.7.2
47
+ auth:
48
+ username: "$DOCKERHUB_USERNAME"
49
+ password: "$DOCKERHUB_PASSWORD"
23
50
  environment:
24
51
  BUNDLE_GEMFILE: Gemfile.rails-6.0
25
52
  working_directory: "~/stitches"
26
53
  steps:
27
54
  - checkout
55
+ - run:
56
+ name: Check for Gemfile.lock presence
57
+ command: ' if (test -f Gemfile.lock) then echo "Dont commit Gemfile.lock (see
58
+ https://github.com/stitchfix/eng-wiki/blob/master/architecture-decisions/0009-rubygem-dependencies-will-be-managed-more-explicitly.md)"
59
+ 1>&2 ; exit 1 ; else exit 0 ; fi '
28
60
  - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
29
61
  - run: bundle install --full-index
30
62
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
@@ -35,18 +67,26 @@ jobs:
35
67
  fi
36
68
  - run:
37
69
  name: Notify Pager Duty
38
- command: bundle exec y-notify "#eng-platform-alerts"
70
+ command: bundle exec y-notify "#devex-alerts"
39
71
  when: on_fail
40
72
  - store_test_results:
41
73
  path: "/tmp/test-results"
42
- ruby-2.5.6-rails-6.0:
74
+ ruby-2.6.6-rails-6.0:
43
75
  docker:
44
- - image: circleci/ruby:2.5.6
76
+ - image: circleci/ruby:2.6.6
77
+ auth:
78
+ username: "$DOCKERHUB_USERNAME"
79
+ password: "$DOCKERHUB_PASSWORD"
45
80
  environment:
46
81
  BUNDLE_GEMFILE: Gemfile.rails-6.0
47
82
  working_directory: "~/stitches"
48
83
  steps:
49
84
  - checkout
85
+ - run:
86
+ name: Check for Gemfile.lock presence
87
+ command: ' if (test -f Gemfile.lock) then echo "Dont commit Gemfile.lock (see
88
+ https://github.com/stitchfix/eng-wiki/blob/master/architecture-decisions/0009-rubygem-dependencies-will-be-managed-more-explicitly.md)"
89
+ 1>&2 ; exit 1 ; else exit 0 ; fi '
50
90
  - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
51
91
  - run: bundle install --full-index
52
92
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
@@ -57,18 +97,26 @@ jobs:
57
97
  fi
58
98
  - run:
59
99
  name: Notify Pager Duty
60
- command: bundle exec y-notify "#eng-platform-alerts"
100
+ command: bundle exec y-notify "#devex-alerts"
61
101
  when: on_fail
62
102
  - store_test_results:
63
103
  path: "/tmp/test-results"
64
- ruby-2.6.4-rails-5.2:
104
+ ruby-2.7.2-rails-5.2:
65
105
  docker:
66
- - image: circleci/ruby:2.6.4
106
+ - image: circleci/ruby:2.7.2
107
+ auth:
108
+ username: "$DOCKERHUB_USERNAME"
109
+ password: "$DOCKERHUB_PASSWORD"
67
110
  environment:
68
111
  BUNDLE_GEMFILE: Gemfile.rails-5.2
69
112
  working_directory: "~/stitches"
70
113
  steps:
71
114
  - checkout
115
+ - run:
116
+ name: Check for Gemfile.lock presence
117
+ command: ' if (test -f Gemfile.lock) then echo "Dont commit Gemfile.lock (see
118
+ https://github.com/stitchfix/eng-wiki/blob/master/architecture-decisions/0009-rubygem-dependencies-will-be-managed-more-explicitly.md)"
119
+ 1>&2 ; exit 1 ; else exit 0 ; fi '
72
120
  - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
73
121
  - run: bundle install --full-index
74
122
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
@@ -79,18 +127,26 @@ jobs:
79
127
  fi
80
128
  - run:
81
129
  name: Notify Pager Duty
82
- command: bundle exec y-notify "#eng-platform-alerts"
130
+ command: bundle exec y-notify "#devex-alerts"
83
131
  when: on_fail
84
132
  - store_test_results:
85
133
  path: "/tmp/test-results"
86
- ruby-2.5.6-rails-5.2:
134
+ ruby-2.6.6-rails-5.2:
87
135
  docker:
88
- - image: circleci/ruby:2.5.6
136
+ - image: circleci/ruby:2.6.6
137
+ auth:
138
+ username: "$DOCKERHUB_USERNAME"
139
+ password: "$DOCKERHUB_PASSWORD"
89
140
  environment:
90
141
  BUNDLE_GEMFILE: Gemfile.rails-5.2
91
142
  working_directory: "~/stitches"
92
143
  steps:
93
144
  - checkout
145
+ - run:
146
+ name: Check for Gemfile.lock presence
147
+ command: ' if (test -f Gemfile.lock) then echo "Dont commit Gemfile.lock (see
148
+ https://github.com/stitchfix/eng-wiki/blob/master/architecture-decisions/0009-rubygem-dependencies-will-be-managed-more-explicitly.md)"
149
+ 1>&2 ; exit 1 ; else exit 0 ; fi '
94
150
  - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
95
151
  - run: bundle install --full-index
96
152
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
@@ -101,7 +157,7 @@ jobs:
101
157
  fi
102
158
  - run:
103
159
  name: Notify Pager Duty
104
- command: bundle exec y-notify "#eng-platform-alerts"
160
+ command: bundle exec y-notify "#devex-alerts"
105
161
  when: on_fail
106
162
  - store_test_results:
107
163
  path: "/tmp/test-results"
@@ -112,31 +168,40 @@ workflows:
112
168
  - release:
113
169
  context: org-global
114
170
  requires:
115
- - ruby-2.6.4-rails-6.0
116
- - ruby-2.5.6-rails-6.0
117
- - ruby-2.6.4-rails-5.2
118
- - ruby-2.5.6-rails-5.2
171
+ - ruby-2.7.2-rails-6.0
172
+ - ruby-2.6.6-rails-6.0
173
+ - ruby-2.7.2-rails-5.2
174
+ - ruby-2.6.6-rails-5.2
175
+ filters:
176
+ tags:
177
+ only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?(RC|rc)[-\.]?\w*)?$/
178
+ branches:
179
+ ignore: /.*/
180
+ - generate-and-push-docs:
181
+ context: org-global
182
+ requires:
183
+ - release
119
184
  filters:
120
185
  tags:
121
- only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?RC[-\.]?\d*)?$/
186
+ only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?(RC|rc)[-\.]?\w*)?$/
122
187
  branches:
123
188
  ignore: /.*/
124
- - ruby-2.6.4-rails-6.0:
189
+ - ruby-2.7.2-rails-6.0:
125
190
  context: org-global
126
191
  filters:
127
192
  tags:
128
193
  only: &1 /.*/
129
- - ruby-2.5.6-rails-6.0:
194
+ - ruby-2.6.6-rails-6.0:
130
195
  context: org-global
131
196
  filters:
132
197
  tags:
133
198
  only: *1
134
- - ruby-2.6.4-rails-5.2:
199
+ - ruby-2.7.2-rails-5.2:
135
200
  context: org-global
136
201
  filters:
137
202
  tags:
138
203
  only: *1
139
- - ruby-2.5.6-rails-5.2:
204
+ - ruby-2.6.6-rails-5.2:
140
205
  context: org-global
141
206
  filters:
142
207
  tags:
@@ -150,11 +215,11 @@ workflows:
150
215
  only:
151
216
  - master
152
217
  jobs:
153
- - ruby-2.6.4-rails-6.0:
218
+ - ruby-2.7.2-rails-6.0:
154
219
  context: org-global
155
- - ruby-2.5.6-rails-6.0:
220
+ - ruby-2.6.6-rails-6.0:
156
221
  context: org-global
157
- - ruby-2.6.4-rails-5.2:
222
+ - ruby-2.7.2-rails-5.2:
158
223
  context: org-global
159
- - ruby-2.5.6-rails-5.2:
224
+ - ruby-2.6.6-rails-5.2:
160
225
  context: org-global
@@ -8,4 +8,4 @@
8
8
  # This file uses the GitHub CODEOWNERS convention to assign PR reviewers:
9
9
  # https://help.github.com/articles/about-codeowners/
10
10
 
11
- * @brettfishman @bwebster
11
+ * @brettfishman @bwebster @stitchfix/devex
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.7.2
@@ -1,7 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.4.4
4
- - 2.5.1
3
+ - 2.6
4
+ - 2.7
5
5
  - ruby-head
6
6
  notifications:
7
7
  email: false
8
+ jobs:
9
+ allow_failures:
10
+ - rvm: ruby-head
data/README.md CHANGED
@@ -4,13 +4,13 @@ Create Microservices in Rails by pretty much just writing regular Rails code.
4
4
 
5
5
  This gem provides:
6
6
 
7
- * transparent API key authentication.
8
- * router-level API version based on headers.
9
- * a way to document your microservice endpoints via acceptance tests.
10
- * structured errors, buildable from invalid Active Records, Exceptions, or by hand.
7
+ - transparent API key authentication.
8
+ - router-level API version based on headers.
9
+ - a way to document your microservice endpoints via acceptance tests.
10
+ - structured errors, buildable from invalid Active Records, Exceptions, or by hand.
11
11
 
12
12
  This, plus much of what you get from Rails already, means you can create a microservice Rails application by just writing the
13
- same Rails code you write today. Instead of rendering web views, you render JSON (which is built into Rails).
13
+ same Rails code you write today. Instead of rendering web views, you render JSON (which is built into Rails).
14
14
 
15
15
  ## To install
16
16
 
@@ -35,14 +35,26 @@ Then, set it up:
35
35
 
36
36
  ### Upgrading from an older version
37
37
 
38
- * If you have a version lower than 3.3.0, you need to run two generators, one of which creates a new database migration on your
39
- `api_clients` table:
38
+ - When upgrading to version 4.0.0 you may now take advantage of an in-memory cache
39
+
40
+ You can enabled it like so
41
+
42
+ ```ruby
43
+ Stitches.configure do |config|
44
+ config.max_cache_ttl = 5 # seconds
45
+ config.max_cache_size = 100 # how many keys to cache
46
+ end
47
+ ```
48
+
49
+ - If you have a version lower than 3.3.0, you need to run two generators, one of which creates a new database migration on your
50
+ `api_clients` table:
40
51
 
41
52
  ```
42
53
  > bin/rails generate stitches:add_enabled_to_api_clients
43
54
  > bin/rails generate stitches:add_deprecation
44
55
  ```
45
- * If you have a version lower than 3.6.0, you need to run one generator:
56
+
57
+ - If you have a version lower than 3.6.0, you need to run one generator:
46
58
 
47
59
  ```
48
60
  > bin/rails generate stitches:add_deprecation
@@ -59,8 +71,8 @@ class Api::V1::WidgetsController < ApiController
59
71
  if widget.valid?
60
72
  head 201
61
73
  else
62
- render json: {
63
- errors: Stitches::Errors.from_active_record_object(widget)
74
+ render json: {
75
+ errors: Stitches::Errors.from_active_record_object(widget)
64
76
  }, status: 422
65
77
  end
66
78
  end
@@ -73,44 +85,62 @@ private
73
85
  end
74
86
  ```
75
87
 
76
- If you think there's nothing special about this—you are correct. This is the vanillaest of vanilla Rails controllers, with a few
88
+ If you think there's nothing special about this—you are correct. This is the vanillaest of vanilla Rails controllers, with a few
77
89
  notable exceptions:
78
90
 
79
- * We aren't checking content type. A stitches-based microservice always uses JSON and refuses to route requests for non-JSON to
80
- you, so there's zero need to use `respond_to` and friends.
81
- * The error-building is structured and reliable.
82
- * This is an authenticated request. No request without proper authentication will be routed here, so you don't have to worry
83
- about it in your code.
84
- * This is a versioned request. While the URL will *not* contain `v1` in it, the `Accept` header will require a version and get
85
- routed here. If you make a V2, it's just a new controller and this concern is handled at the routing layer.
91
+ - We aren't checking content type. A stitches-based microservice always uses JSON and refuses to route requests for non-JSON to
92
+ you, so there's zero need to use `respond_to` and friends.
93
+ - The error-building is structured and reliable.
94
+ - This is an authenticated request. No request without proper authentication will be routed here, so you don't have to worry
95
+ about it in your code.
96
+ - This is a versioned request. While the URL will _not_ contain `v1` in it, the `Accept` header will require a version and get
97
+ routed here. If you make a V2, it's just a new controller and this concern is handled at the routing layer.
86
98
 
87
- All this means that the Rails skills of you and your team can be directly applied to building microservices. You don't have to make a bunch of boring decisions about auth, versioning, or content-types. It also means you can start deploying and creating microservices with little friction. No need to deal with a complex DSL or new programming language to get yourselves going with Microservices.
99
+ All this means that the Rails skills of you and your team can be directly applied to building microservices. You don't have to make a bunch of boring decisions about auth, versioning, or content-types. It also means you can start deploying and creating microservices with little friction. No need to deal with a complex DSL or new programming language to get yourselves going with Microservices.
88
100
 
89
101
  ## More Info
90
102
 
91
103
  See [the wiki](https://github.com/stitchfix/stitches/wiki/Setup) for how to setup stitches.
92
104
 
93
- * [Stitches Features](https://github.com/stitchfix/stitches/wiki/Features-of-Stitches) include:
105
+ - [Stitches Features](https://github.com/stitchfix/stitches/wiki/Features-of-Stitches) include:
94
106
  - Authorization via API key
95
107
  - Versioned requests via HTTP content types
96
108
  - Structured Errors
97
109
  - ISO 8601-formatted dates
98
110
  - Deprecation using the `Sunset` header
99
- * The [Generator](https://github.com/stitchfix/stitches/wiki/Generator) sets up some code in your app, so you can start writing
100
- APIs using vanilla Rails idioms:
111
+ - An optional ApiKey cache to allow mostly DB free APIs
112
+ - The [Generator](https://github.com/stitchfix/stitches/wiki/Generator) sets up some code in your app, so you can start writing
113
+ APIs using vanilla Rails idioms:
101
114
  - a "ping" controller that can validate your app is working
102
115
  - version routing based on content-type (requests for V2 use the same URL, but are serviced by a different controller)
103
116
  - An ApiClient Active Record
104
117
  - Acceptance tests that can produce API documentation as they test your app.
105
- * Stitches provides [testing support](https://github.com/stitchfix/stitches/wiki/Testing)
118
+ - Stitches provides [testing support](https://github.com/stitchfix/stitches/wiki/Testing)
119
+
120
+ ## API Key Caching
121
+
122
+ Since version 4.0.0, stitches now has the ability to cache API keys in
123
+ memory for a configurable amount of time. This may be an improvement for
124
+ some applications.
125
+
126
+ You must configure the API Cache for it be used.
127
+
128
+ ```ruby
129
+ Stitches.configure do |config|
130
+ config.max_cache_ttl = 5 # seconds
131
+ config.max_cache_size = 100 # how many keys to cache
132
+ end
133
+ ```
106
134
 
135
+ Your cache size should be
136
+ larger then the number of consumer keys your service has.
107
137
 
108
138
  ## Developing
109
139
 
110
- Although `Stitches.configuration` is global, do not depend directly on that in your logic. Instead, allow all classes to receive a configuration object in their constructor. This makes the classes easier to deal with and change, without incurring much of a real cost to development. Global symbols suck, but are convenient. This is how you make the most of it.
140
+ Although `Stitches.configuration` is global, do not depend directly on that in your logic. Instead, allow all classes to receive a configuration object in their constructor. This makes the classes easier to deal with and change, without incurring much of a real cost to development. Global symbols suck, but are convenient. This is how you make the most of it.
111
141
 
112
142
  Also, the integration test does a lot of "testing the implementation", but since Rails generators are notorious for silently
113
- failing with a successful result, we have to make sure that the various `inject_into_file` calls are actually working. Do not do
143
+ failing with a successful result, we have to make sure that the various `inject_into_file` calls are actually working. Do not do
114
144
  any fancy refactors here, just keep it up to date.
115
145
 
116
146
  ---
@@ -2,7 +2,6 @@ module Stitches
2
2
  # A middleware that will skip its behavior if the path matches an allowed URL
3
3
  class AllowlistMiddleware
4
4
  def initialize(app, options={})
5
-
6
5
  @app = app
7
6
  @configuration = options[:configuration] || Stitches.configuration
8
7
  @except = options[:except] || @configuration.allowlist_regexp
@@ -0,0 +1,42 @@
1
+ require 'lru_redux'
2
+
3
+ module Stitches::ApiClientAccessWrapper
4
+
5
+ def self.fetch_for_key(key)
6
+ if cache_enabled
7
+ fetch_for_key_from_cache(key)
8
+ else
9
+ fetch_for_key_from_db(key)
10
+ end
11
+ end
12
+
13
+ def self.fetch_for_key_from_cache(key)
14
+ api_key_cache.getset(key) do
15
+ fetch_for_key_from_db(key)
16
+ end
17
+ end
18
+
19
+ def self.fetch_for_key_from_db(key)
20
+ if ::ApiClient.column_names.include?("enabled")
21
+ ::ApiClient.find_by(key: key, enabled: true)
22
+ else
23
+ ActiveSupport::Deprecation.warn('api_keys is missing "enabled" column. Run "rails g stitches:add_enabled_to_api_clients"')
24
+ ::ApiClient.find_by(key: key)
25
+ end
26
+ end
27
+
28
+ def self.clear_api_cache
29
+ api_key_cache.clear if cache_enabled
30
+ end
31
+
32
+ def self.api_key_cache
33
+ @api_key_cache ||= LruRedux::TTL::ThreadSafeCache.new(
34
+ Stitches.configuration.max_cache_size,
35
+ Stitches.configuration.max_cache_ttl,
36
+ )
37
+ end
38
+
39
+ def self.cache_enabled
40
+ Stitches.configuration.max_cache_ttl.positive?
41
+ end
42
+ end
@@ -12,7 +12,6 @@ module Stitches
12
12
 
13
13
  desc "Bootstraps your API service with a basic ping controller and spec to ensure everything is setup properly"
14
14
  def bootstrap_api
15
- gem "stitches"
16
15
  gem "apitome"
17
16
  gem_group :development, :test do
18
17
  gem "rspec"
@@ -20,7 +19,9 @@ module Stitches
20
19
  gem "rspec_api_documentation"
21
20
  end
22
21
 
23
- run "bundle install"
22
+ Bundler.with_clean_env do
23
+ run "bundle install"
24
+ end
24
25
  generate "apitome:install"
25
26
  generate "rspec:install"
26
27
 
@@ -20,11 +20,6 @@ module Stitches
20
20
  # ApiClient that it maps to.
21
21
  class ApiKey < Stitches::AllowlistMiddleware
22
22
 
23
- def initialize(app,options = {})
24
- super(app,options)
25
- @realm = rails_app_module
26
- end
27
-
28
23
  protected
29
24
 
30
25
  def do_call(env)
@@ -32,26 +27,19 @@ module Stitches
32
27
  if authorization
33
28
  if authorization =~ /#{@configuration.custom_http_auth_scheme}\s+key=(.*)\s*$/
34
29
  key = $1
35
-
36
- if ApiClient.column_names.include?("enabled")
37
- client = ApiClient.where(key: key, enabled: true).first
38
- else
39
- ActiveSupport::Deprecation.warn('api_keys is missing "enabled" column. Run "rails g stitches:add_enabled_to_api_clients"')
40
- client = ApiClient.where(key: key).first
41
- end
42
-
30
+ client = Stitches::ApiClientAccessWrapper.fetch_for_key(key)
43
31
  if client.present?
44
32
  env[@configuration.env_var_to_hold_api_client_primary_key] = client.id
45
33
  env[@configuration.env_var_to_hold_api_client] = client
46
34
  @app.call(env)
47
35
  else
48
- UnauthorizedResponse.new("key invalid",@realm,@configuration.custom_http_auth_scheme)
36
+ unauthorized_response("key invalid")
49
37
  end
50
38
  else
51
- UnauthorizedResponse.new("bad authorization type",@realm,@configuration.custom_http_auth_scheme)
39
+ unauthorized_response("bad authorization type")
52
40
  end
53
41
  else
54
- UnauthorizedResponse.new("no authorization header",@realm,@configuration.custom_http_auth_scheme)
42
+ unauthorized_response("no authorization header")
55
43
  end
56
44
  end
57
45
 
@@ -68,10 +56,11 @@ module Stitches
68
56
  parent.to_s
69
57
  end
70
58
 
71
- class UnauthorizedResponse < Rack::Response
72
- def initialize(reason,realm,custom_http_auth_scheme)
73
- super("Unauthorized - #{reason}", 401, { "WWW-Authenticate" => "#{custom_http_auth_scheme} realm=#{realm}" })
74
- end
59
+ def unauthorized_response(reason)
60
+ status = 401
61
+ body = "Unauthorized - #{reason}"
62
+ header = { "WWW-Authenticate" => "#{@configuration.custom_http_auth_scheme} realm=#{rails_app_module}" }
63
+ Rack::Response.new(body, status, header).finish
75
64
  end
76
65
 
77
66
  end
@@ -13,6 +13,8 @@ class Stitches::Configuration
13
13
  @custom_http_auth_scheme = UnsetString.new("custom_http_auth_scheme")
14
14
  @env_var_to_hold_api_client_primary_key = NonNullString.new("env_var_to_hold_api_client_primary_key","STITCHES_API_CLIENT_ID")
15
15
  @env_var_to_hold_api_client= NonNullString.new("env_var_to_hold_api_client","STITCHES_API_CLIENT")
16
+ @max_cache_ttl = NonNullInteger.new("max_cache_ttl", 0)
17
+ @max_cache_size = NonNullInteger.new("max_cache_size", 0)
16
18
  end
17
19
 
18
20
  # A RegExp that allows URLS around the mime type and api key requirements.
@@ -25,11 +27,6 @@ class Stitches::Configuration
25
27
  @allowlist_regexp = new_allowlist_regexp
26
28
  end
27
29
 
28
- def whitelist_regexp=(new_allowlist_regexp)
29
- self.allowlist_regexp = new_allowlist_regexp
30
- warn("⚠️ 'whitelist' is deprecated in stitches configuration, please use 'allowlist' or auto-update with:\n\n bin/rails g stitches:update_configuration\n\n⚠️ 'whitelist' will be removed in 4.0")
31
- end
32
-
33
30
  # The name of your custom http auth scheme. This must be set, and has no default
34
31
  def custom_http_auth_scheme
35
32
  @custom_http_auth_scheme.to_s
@@ -39,7 +36,7 @@ class Stitches::Configuration
39
36
  @custom_http_auth_scheme = NonNullString.new("custom_http_auth_scheme",new_custom_http_auth_scheme)
40
37
  end
41
38
 
42
- # The name of the environment variable that the ApiKey middleware should use to
39
+ # The name of the environment variable that the ApiKey middleware should use to
43
40
  # place the primary key of the authenticated ApiKey. For example, if a user provides
44
41
  # the api key 1234-1234-1234-1234, and that maps to the primary key 42 in your database,
45
42
  # the environment will contain "42" in the key provided here.
@@ -59,8 +56,40 @@ class Stitches::Configuration
59
56
  @env_var_to_hold_api_client= NonNullString.new("env_var_to_hold_api_client",new_env_var_to_hold_api_client)
60
57
  end
61
58
 
59
+ def max_cache_ttl
60
+ @max_cache_ttl.to_i
61
+ end
62
+
63
+ def max_cache_ttl=(new_max_cache_ttl)
64
+ @max_cache_ttl = NonNullInteger.new("max_cache_ttl", new_max_cache_ttl)
65
+ end
66
+
67
+ def max_cache_size
68
+ @max_cache_size.to_i
69
+ end
70
+
71
+ def max_cache_size=(new_max_cache_size)
72
+ @max_cache_size = NonNullInteger.new("max_cache_size", new_max_cache_size)
73
+ end
74
+
62
75
  private
63
76
 
77
+ class NonNullInteger
78
+ def initialize(name, value)
79
+ unless value.is_a?(Integer)
80
+ raise "#{name} must be an Integer, not a #{value.class}"
81
+ end
82
+
83
+ @value = value
84
+ end
85
+
86
+ def to_i
87
+ @value
88
+ end
89
+
90
+ alias to_integer to_i
91
+ end
92
+
64
93
  class NonNullString
65
94
  def initialize(name,string)
66
95
  unless string.nil? || string.is_a?(String)
@@ -11,4 +11,14 @@ Stitches.configure do |configuration|
11
11
  # Env var that gets the primary key of the authenticated ApiKey
12
12
  # for access in your controllers, so they don't need to re-parse the header
13
13
  # configuration.env_var_to_hold_api_client_primary_key = "YOUR_ENV_VAR"
14
+
15
+ # Configures how long to cache ApiKeys in memory (In Seconds)
16
+ # A value of 0 will disable the cache entierly
17
+ # Default is 0
18
+ # configuration.max_cache_ttl = 5
19
+
20
+ # Configures how many ApiKeys to cache at one time
21
+ # This should be larger then the number of clients
22
+ # Default is 0
23
+ # configuration.max_cache_size = 100
14
24
  end
@@ -1,9 +1,11 @@
1
1
  require 'stitches/api_key'
2
2
  require 'stitches/valid_mime_type'
3
+ require 'stitches/api_client_access_wrapper'
3
4
 
4
5
  module Stitches
5
6
  class Railtie < Rails::Railtie
6
7
  config.app_middleware.use Stitches::ApiKey
7
8
  config.app_middleware.use Stitches::ValidMimeType
9
+
8
10
  end
9
11
  end
@@ -1,31 +1,39 @@
1
1
  require_relative 'allowlist_middleware'
2
2
  module Stitches
3
- # A middleware that requires all API calls to be for versioned JSON. This means that the Accept
4
- # header (available to Rack apps as HTTP_ACCEPT) should be like so:
3
+ # A middleware that requires all API calls to be for versioned JSON or Protobuf.
4
+ #
5
+ # This means that the Accept header (available to Rack apps as HTTP_ACCEPT) should be like so:
5
6
  #
6
7
  # application/json; version=1
7
8
  #
8
9
  # This just checks that you've specified some numeric version. ApiVersionConstraint should be used
9
10
  # to "lock down" the versions you accept.
11
+ #
12
+ # Or in the case of a protobuf encoded payload the header should be like so:
13
+ #
14
+ # application/protobuf
15
+ #
16
+ # There isn't an accepted standard for protobuf encoded payloads but this form is common.
10
17
  class ValidMimeType < Stitches::AllowlistMiddleware
11
18
 
12
19
  protected
13
20
 
14
21
  def do_call(env)
15
22
  accept = String(env["HTTP_ACCEPT"])
16
- if accept =~ %r{application/json} && accept =~ %r{version=\d+}
23
+ if (accept =~ %r{application/json} && accept =~ %r{version=\d+}) || accept =~ %r{application/protobuf}
17
24
  @app.call(env)
18
25
  else
19
- NotAcceptableResponse.new(accept)
26
+ not_acceptable_response(accept)
20
27
  end
21
28
  end
22
29
 
23
30
  private
24
31
 
25
- class NotAcceptableResponse < Rack::Response
26
- def initialize(accept_header)
27
- super("Not Acceptable - '#{accept_header}' didn't have the right mime type or version number. We only accept application/json with a version", 406)
28
- end
32
+ def not_acceptable_response(accept_header)
33
+ status = 406
34
+ body = "Not Acceptable - '#{accept_header}' didn't have the right mime type or version number. We only accept application/json with a version or application/protobuf"
35
+ header = { "WWW-Authenticate" => accept_header }
36
+ Rack::Response.new(body, status, header).finish
29
37
  end
30
38
 
31
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Stitches
2
- VERSION = '3.8.1'
4
+ VERSION = '4.0.1'
3
5
  end
@@ -14,7 +14,6 @@ require 'stitches/errors'
14
14
  require 'stitches/api_generator'
15
15
  require 'stitches/add_deprecation_generator'
16
16
  require 'stitches/add_enabled_to_api_clients_generator'
17
- require 'stitches/update_configuration_generator'
18
17
  require 'stitches/api_version_constraint'
19
18
  require 'stitches/api_key'
20
19
  require 'stitches/deprecation'
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "owners": [
3
3
  {
4
- "team": "platform"
4
+ "team": "devex"
5
5
  }
6
6
  ]
7
7
  }
@@ -0,0 +1,52 @@
1
+ require 'spec_helper.rb'
2
+
3
+ module MyApp
4
+ class Application
5
+ end
6
+ end
7
+
8
+ unless defined? ApiClient
9
+ class ApiClient
10
+ def self.column_names
11
+ ["enabled"]
12
+ end
13
+ end
14
+ end
15
+
16
+ describe Stitches::ApiClientAccessWrapper do
17
+ let(:api_client) {
18
+ double(ApiClient, id: 42)
19
+ }
20
+ before do
21
+ Stitches.configuration.reset_to_defaults!
22
+ end
23
+ describe '#fetch_by_key' do
24
+ context "cache is disabled" do
25
+ before do
26
+ expect(ApiClient).to receive(:find_by).and_return(api_client).twice
27
+ end
28
+
29
+ it "fetchs object from db twice" do
30
+ expect(described_class.fetch_for_key("123").id).to eq(42)
31
+ expect(described_class.fetch_for_key("123").id).to eq(42)
32
+ end
33
+ end
34
+
35
+ context "cache is configured" do
36
+ before do
37
+ Stitches.configure do |config|
38
+ config.max_cache_ttl = 5
39
+ config.max_cache_size = 10
40
+ end
41
+
42
+ expect(ApiClient).to receive(:find_by).and_return(api_client).once
43
+ end
44
+
45
+ it "fetchs object from cache" do
46
+ expect(described_class.fetch_for_key("123").id).to eq(42)
47
+ # This should hit the cache
48
+ expect(described_class.fetch_for_key("123").id).to eq(42)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -15,10 +15,8 @@ end
15
15
 
16
16
  describe Stitches::ApiKey do
17
17
  let(:app) { double("rack app") }
18
- let(:api_clients) {
19
- [
20
- double(ApiClient, id: 42)
21
- ]
18
+ let(:api_client) {
19
+ double(ApiClient, id: 42)
22
20
  }
23
21
 
24
22
  before do
@@ -27,23 +25,27 @@ describe Stitches::ApiKey do
27
25
  fake_rails_app = MyApp::Application.new
28
26
  allow(Rails).to receive(:application).and_return(fake_rails_app)
29
27
  allow(app).to receive(:call).with(env)
30
- allow(ApiClient).to receive(:where).and_return(api_clients)
28
+ allow(ApiClient).to receive(:find_by).and_return(api_client)
29
+ Stitches::ApiClientAccessWrapper.clear_api_cache
31
30
  end
32
31
 
33
32
  subject(:middleware) { described_class.new(app, namespace: "/api") }
34
33
 
35
34
  shared_examples "an unauthorized response" do
36
35
  it "returns a 401" do
37
- expect(@response.status).to eq(401)
36
+ status, _headers, _body = @response
37
+ expect(status).to eq(401)
38
38
  end
39
39
  it "sets the proper header" do
40
- expect(@response.headers["WWW-Authenticate"]).to eq("MyAwesomeInternalScheme realm=MyApp")
40
+ _status, headers, _body = @response
41
+ expect(headers["WWW-Authenticate"]).to eq("MyAwesomeInternalScheme realm=MyApp")
41
42
  end
42
43
  it "stops the call chain preventing anything from happening" do
43
44
  expect(app).not_to have_received(:call)
44
45
  end
45
46
  it "sends a reasonable message" do
46
- expect(@response.body).to eq([expected_body])
47
+ _status, _headers, body = @response
48
+ expect(body).to eq([expected_body])
47
49
  end
48
50
  end
49
51
 
@@ -155,18 +157,17 @@ describe Stitches::ApiKey do
155
157
  end
156
158
 
157
159
  it "sets the api_client's ID in the environment" do
158
- expect(env[Stitches.configuration.env_var_to_hold_api_client_primary_key]).to eq(api_clients.first.id)
160
+ expect(env[Stitches.configuration.env_var_to_hold_api_client_primary_key]).to eq(api_client.id)
159
161
  end
160
162
 
161
163
  it "sets the api_client itself in the environment" do
162
- expect(env[Stitches.configuration.env_var_to_hold_api_client]).to eq(api_clients.first)
164
+ expect(env[Stitches.configuration.env_var_to_hold_api_client]).to eq(api_client)
163
165
  end
164
166
  end
165
167
 
166
168
  context "unauthorized responses" do
167
169
  before do
168
170
  @response = middleware.call(env)
169
- @response.finish
170
171
  end
171
172
  context "invalid key" do
172
173
  let(:env) {
@@ -175,7 +176,7 @@ describe Stitches::ApiKey do
175
176
  "HTTP_AUTHORIZATION" => "MyAwesomeInternalScheme key=foobar",
176
177
  }
177
178
  }
178
- let(:api_clients) { [] }
179
+ let(:api_client) { nil }
179
180
 
180
181
  it_behaves_like "an unauthorized response" do
181
182
  let(:expected_body) { "Unauthorized - key invalid" }
@@ -9,17 +9,23 @@ describe Stitches::Configuration do
9
9
  let(:allowlist_regexp) { %r{foo} }
10
10
  let(:custom_http_auth_scheme) { "Blah" }
11
11
  let(:env_var_to_hold_api_client_primary_key) { "FOOBAR" }
12
+ let(:max_cache_ttl) { 11 }
13
+ let(:max_cache_size) { 111 }
12
14
 
13
15
  it "can be configured globally" do
14
16
  Stitches.configure do |config|
15
17
  config.allowlist_regexp = allowlist_regexp
16
18
  config.custom_http_auth_scheme = custom_http_auth_scheme
17
19
  config.env_var_to_hold_api_client_primary_key = env_var_to_hold_api_client_primary_key
20
+ config.max_cache_ttl = max_cache_ttl
21
+ config.max_cache_size = max_cache_size
18
22
  end
19
23
 
20
24
  expect(Stitches.configuration.allowlist_regexp).to eq(allowlist_regexp)
21
25
  expect(Stitches.configuration.custom_http_auth_scheme).to eq(custom_http_auth_scheme)
22
26
  expect(Stitches.configuration.env_var_to_hold_api_client_primary_key).to eq(env_var_to_hold_api_client_primary_key)
27
+ expect(Stitches.configuration.max_cache_ttl).to eq(max_cache_ttl)
28
+ expect(Stitches.configuration.max_cache_size).to eq(max_cache_size)
23
29
  end
24
30
 
25
31
  it "defaults to nil for allowlist_regexp" do
@@ -30,6 +36,14 @@ describe Stitches::Configuration do
30
36
  expect(Stitches.configuration.env_var_to_hold_api_client_primary_key).to eq("STITCHES_API_CLIENT_ID")
31
37
  end
32
38
 
39
+ it "defaults to 0 for max_cache_ttl" do
40
+ expect(Stitches.configuration.max_cache_ttl).to eq(0)
41
+ end
42
+
43
+ it "sets a default for max_cache_size" do
44
+ expect(Stitches.configuration.max_cache_size).to eq(0)
45
+ end
46
+
33
47
  it "blows up if you try to use custom_http_auth_scheme without having set it" do
34
48
  expect {
35
49
  Stitches.configuration.custom_http_auth_scheme
@@ -102,19 +116,34 @@ describe Stitches::Configuration do
102
116
  }.not_to raise_error
103
117
  end
104
118
  end
105
- context "deprecated options we want to support for backwards compatibility" do
106
119
 
107
- let(:logger) { double("logger") }
108
- before do
109
- allow(Rails).to receive(:logger).and_return(logger)
110
- allow(logger).to receive(:info)
120
+ describe "max_cache_ttl" do
121
+ let(:config) { Stitches::Configuration.new }
122
+ it "must be an integer" do
123
+ expect {
124
+ config.max_cache_ttl = ""
125
+ }.to raise_error(/max_cache_ttl must be an Integer, not a String/)
111
126
  end
112
127
 
113
- it "'whitelist' still works for allowlist" do
114
- Stitches.configure do |config|
115
- config.whitelist_regexp = /foo/
116
- end
117
- expect(Stitches.configuration.allowlist_regexp).to eq(/foo/)
128
+ it "may not be nil" do
129
+ expect {
130
+ config.max_cache_ttl = nil
131
+ }.to raise_error(/max_cache_ttl must be an Integer, not a NilClass/)
132
+ end
133
+ end
134
+
135
+ describe "max_cache_size" do
136
+ let(:config) { Stitches::Configuration.new }
137
+ it "must be an integer" do
138
+ expect {
139
+ config.max_cache_size = ""
140
+ }.to raise_error(/max_cache_size must be an Integer, not a String/)
141
+ end
142
+
143
+ it "may not be nil" do
144
+ expect {
145
+ config.max_cache_size = nil
146
+ }.to raise_error(/max_cache_size must be an Integer, not a NilClass/)
118
147
  end
119
148
  end
120
149
  end
@@ -109,35 +109,6 @@ RSpec.describe "Adding Stitches to a New Rails App", :integration do
109
109
  expect(include_line).to_not be_nil,lines.inspect
110
110
  end
111
111
 
112
- it "inserts can update old configuration" do
113
- run "bin/rails generate stitches:api"
114
-
115
- initializer = rails_root / "config" / "initializers" / "stitches.rb"
116
-
117
- initializer_contents = File.read(initializer).split(/\n/)
118
- found_initializer = false
119
- File.open(initializer,"w") do |file|
120
- initializer_contents.each do |line|
121
- if line =~ /allowlist/
122
- line = line.gsub("allowlist","whitelist")
123
- found_initializer = true
124
- end
125
- file.puts line
126
- end
127
- end
128
-
129
- raise "Didn't find 'allowlist' in the initializer?!" if !found_initializer
130
-
131
- run "bin/rails generate stitches:update_configuration"
132
-
133
- lines = File.read(initializer).split(/\n/)
134
- include_line = lines.detect { |line|
135
- line =~ /whitelist/
136
- }
137
-
138
- expect(include_line).to be_nil,lines.inspect
139
- end
140
-
141
112
  class RoutesFileAnalysis
142
113
  attr_reader :routes_file
143
114
  def initialize(routes_file, namespace: nil, module_scope: nil, resource: nil, mounted_engine: nil)
@@ -11,13 +11,15 @@ describe Stitches::ValidMimeType do
11
11
 
12
12
  shared_examples "an unacceptable response" do
13
13
  it "returns a 406" do
14
- expect(@response.status).to eq(406)
14
+ status, _headers, _body = @response
15
+ expect(status).to eq(406)
15
16
  end
16
17
  it "stops the call chain preventing anything from happening" do
17
18
  expect(app).not_to have_received(:call)
18
19
  end
19
20
  it "sends a reasonable message" do
20
- expect(@response.body.first).to match(/didn't have the right mime type or version number. We only accept application\/json/)
21
+ _status, _headers, body = @response
22
+ expect(body.first).to match(/didn't have the right mime type or version number. We only accept application\/json/)
21
23
  end
22
24
  end
23
25
 
@@ -130,10 +132,25 @@ describe Stitches::ValidMimeType do
130
132
  end
131
133
  end
132
134
 
135
+ context "protbuf mime type" do
136
+ let(:env) {
137
+ {
138
+ "PATH_INFO" => "/api/ping",
139
+ "HTTP_ACCEPT" => "application/protobuf",
140
+ }
141
+ }
142
+
143
+ before do
144
+ @response = middleware.call(env)
145
+ end
146
+ it "calls through to the rest of the chain" do
147
+ expect(app).to have_received(:call).with(env)
148
+ end
149
+ end
150
+
133
151
  context "unacceptable responses" do
134
152
  before do
135
153
  @response = middleware.call(env)
136
- @response.finish
137
154
  end
138
155
  context "no header" do
139
156
  let(:env) {
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.add_runtime_dependency("rails")
22
22
  s.add_runtime_dependency("pg")
23
+ s.add_runtime_dependency("lru_redux")
23
24
 
24
25
  s.add_development_dependency("rspec", ">= 3")
25
26
  s.add_development_dependency("rake")
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stitches
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.1
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stitch Fix Engineering
8
8
  - Andrew Peterson
9
9
  - Dave Copeland
10
10
  - Jonathan Dean
11
- autorequire:
11
+ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2019-09-10 00:00:00.000000000 Z
14
+ date: 2020-11-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rails
@@ -41,6 +41,20 @@ dependencies:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: lru_redux
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
44
58
  - !ruby/object:Gem::Dependency
45
59
  name: rspec
46
60
  requirement: !ruby/object:Gem::Requirement
@@ -95,11 +109,12 @@ extensions: []
95
109
  extra_rdoc_files: []
96
110
  files:
97
111
  - ".circleci/config.yml"
112
+ - ".github/CODEOWNERS"
113
+ - ".github/PULL_REQUEST_TEMPLATE.md"
98
114
  - ".gitignore"
99
115
  - ".ruby-gemset"
100
116
  - ".ruby-version"
101
117
  - ".travis.yml"
102
- - CODEOWNERS
103
118
  - CODE_OF_CONDUCT.md
104
119
  - CONTRIBUTING.md
105
120
  - Gemfile
@@ -109,7 +124,6 @@ files:
109
124
  - Gemfile.rails-5.2
110
125
  - Gemfile.rails-6.0
111
126
  - LICENSE.txt
112
- - PULL_REQUEST_TEMPLATE.md
113
127
  - README.md
114
128
  - Rakefile
115
129
  - build-matrix.json
@@ -117,6 +131,7 @@ files:
117
131
  - lib/stitches/add_deprecation_generator.rb
118
132
  - lib/stitches/add_enabled_to_api_clients_generator.rb
119
133
  - lib/stitches/allowlist_middleware.rb
134
+ - lib/stitches/api_client_access_wrapper.rb
120
135
  - lib/stitches/api_generator.rb
121
136
  - lib/stitches/api_key.rb
122
137
  - lib/stitches/api_version_constraint.rb
@@ -147,12 +162,11 @@ files:
147
162
  - lib/stitches/spec/have_api_error.rb
148
163
  - lib/stitches/spec/show_deprecation.rb
149
164
  - lib/stitches/spec/test_headers.rb
150
- - lib/stitches/update_configuration_generator.rb
151
165
  - lib/stitches/valid_mime_type.rb
152
166
  - lib/stitches/version.rb
153
- - lib/stitches/whitelisting_middleware.rb
154
167
  - lib/stitches_norailtie.rb
155
168
  - owners.json
169
+ - spec/api_client_access_wrapper_spec.rb
156
170
  - spec/api_key_spec.rb
157
171
  - spec/api_version_constraint_spec.rb
158
172
  - spec/configuration_spec.rb
@@ -170,7 +184,7 @@ homepage: https://github.com/stitchfix/stitches
170
184
  licenses:
171
185
  - MIT
172
186
  metadata: {}
173
- post_install_message:
187
+ post_install_message:
174
188
  rdoc_options: []
175
189
  require_paths:
176
190
  - lib
@@ -185,11 +199,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
199
  - !ruby/object:Gem::Version
186
200
  version: '0'
187
201
  requirements: []
188
- rubygems_version: 3.0.3
189
- signing_key:
202
+ rubygems_version: 3.1.4
203
+ signing_key:
190
204
  specification_version: 4
191
205
  summary: You'll be in stitches at how easy it is to create a service at Stitch Fix
192
206
  test_files:
207
+ - spec/api_client_access_wrapper_spec.rb
193
208
  - spec/api_key_spec.rb
194
209
  - spec/api_version_constraint_spec.rb
195
210
  - spec/configuration_spec.rb
@@ -1,16 +0,0 @@
1
- require 'rails/generators'
2
-
3
- module Stitches
4
- class UpdateConfigurationGenerator < Rails::Generators::Base
5
- include Rails::Generators::Migration
6
-
7
- source_root(File.expand_path(File.join(File.dirname(__FILE__),"generator_files")))
8
-
9
- desc "Change your configuration to use 'allowlist' so you'll be ready for 4.x"
10
- def update_to_allowlist
11
- gsub_file "config/initializers/stitches.rb", /whitelist/, "allowlist"
12
- puts "🎉 You are now good to go!"
13
- end
14
-
15
- end
16
- end
@@ -1,5 +0,0 @@
1
- require_relative "allowlist_middleware"
2
-
3
- module Stitches
4
- WhitelistingMiddleware = AllowlistMiddleware
5
- end