stitches 3.8.0 → 4.0.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/.circleci/config.yml +30 -30
- data/{CODEOWNERS → .github/CODEOWNERS} +1 -1
- data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +0 -0
- data/.ruby-version +1 -1
- data/.travis.yml +5 -2
- data/Gemfile.rails-5.1 +1 -2
- data/Gemfile.rails-5.2 +1 -2
- data/Gemfile.rails-6.0 +7 -0
- data/README.md +55 -25
- data/lib/stitches/allowlist_middleware.rb +0 -1
- data/lib/stitches/api_client_access_wrapper.rb +42 -0
- data/lib/stitches/api_generator.rb +3 -2
- data/lib/stitches/api_key.rb +20 -20
- data/lib/stitches/configuration.rb +35 -6
- data/lib/stitches/generator_files/config/initializers/stitches.rb +10 -0
- data/lib/stitches/generator_files/spec/features/api_spec.rb.erb +10 -2
- data/lib/stitches/railtie.rb +2 -0
- data/lib/stitches/valid_mime_type.rb +6 -5
- data/lib/stitches/version.rb +3 -1
- data/lib/stitches_norailtie.rb +0 -1
- data/owners.json +1 -1
- data/spec/api_client_access_wrapper_spec.rb +52 -0
- data/spec/api_key_spec.rb +13 -12
- data/spec/configuration_spec.rb +39 -10
- data/spec/integration/add_to_rails_app_spec.rb +0 -29
- data/spec/valid_mime_type_spec.rb +4 -3
- data/stitches.gemspec +1 -0
- metadata +23 -7
- data/lib/stitches/update_configuration_generator.rb +0 -16
- data/lib/stitches/whitelisting_middleware.rb +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ea8633778064ab7646860c9a0682a7290bfbea3d6f3f091c290f50d22f97de2
|
|
4
|
+
data.tar.gz: 0eb1d04f15fc37ab0b0484ae6a73f953426cc5dd1b9efd056e42300caa4c2d04
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f854d4c3feca2e48b6edfde3174564112b2c0c1d6cb6b93bed960985cc779c1119bcf5ddb65d43d85d3c349d9f440b61e14b1cef27aa6f8c1701768ab5ac1dd5
|
|
7
|
+
data.tar.gz: b19d1b967d4a5e0dc57265475683bbfd8e9f255d15ee0af1a8130832d16e73e54b52b7e34fc025fe170a4322cb30cbddb6f2e631f7c81521953e4e48514bb547
|
data/.circleci/config.yml
CHANGED
|
@@ -5,7 +5,7 @@ version: 2
|
|
|
5
5
|
jobs:
|
|
6
6
|
release:
|
|
7
7
|
docker:
|
|
8
|
-
- image: circleci/ruby:2.
|
|
8
|
+
- image: circleci/ruby:2.7.1
|
|
9
9
|
steps:
|
|
10
10
|
- checkout
|
|
11
11
|
- run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
|
|
@@ -17,11 +17,11 @@ jobs:
|
|
|
17
17
|
- run:
|
|
18
18
|
name: Build/release gem to artifactory
|
|
19
19
|
command: bundle exec rake push_artifactory
|
|
20
|
-
ruby-2.
|
|
20
|
+
ruby-2.7.1-rails-6.0:
|
|
21
21
|
docker:
|
|
22
|
-
- image: circleci/ruby:2.
|
|
22
|
+
- image: circleci/ruby:2.7.1
|
|
23
23
|
environment:
|
|
24
|
-
BUNDLE_GEMFILE: Gemfile.rails-
|
|
24
|
+
BUNDLE_GEMFILE: Gemfile.rails-6.0
|
|
25
25
|
working_directory: "~/stitches"
|
|
26
26
|
steps:
|
|
27
27
|
- checkout
|
|
@@ -35,15 +35,15 @@ jobs:
|
|
|
35
35
|
fi
|
|
36
36
|
- run:
|
|
37
37
|
name: Notify Pager Duty
|
|
38
|
-
command: bundle exec y-notify "#
|
|
38
|
+
command: bundle exec y-notify "#devex-alerts"
|
|
39
39
|
when: on_fail
|
|
40
40
|
- store_test_results:
|
|
41
41
|
path: "/tmp/test-results"
|
|
42
|
-
ruby-2.
|
|
42
|
+
ruby-2.6.6-rails-6.0:
|
|
43
43
|
docker:
|
|
44
|
-
- image: circleci/ruby:2.
|
|
44
|
+
- image: circleci/ruby:2.6.6
|
|
45
45
|
environment:
|
|
46
|
-
BUNDLE_GEMFILE: Gemfile.rails-
|
|
46
|
+
BUNDLE_GEMFILE: Gemfile.rails-6.0
|
|
47
47
|
working_directory: "~/stitches"
|
|
48
48
|
steps:
|
|
49
49
|
- checkout
|
|
@@ -57,15 +57,15 @@ jobs:
|
|
|
57
57
|
fi
|
|
58
58
|
- run:
|
|
59
59
|
name: Notify Pager Duty
|
|
60
|
-
command: bundle exec y-notify "#
|
|
60
|
+
command: bundle exec y-notify "#devex-alerts"
|
|
61
61
|
when: on_fail
|
|
62
62
|
- store_test_results:
|
|
63
63
|
path: "/tmp/test-results"
|
|
64
|
-
ruby-2.
|
|
64
|
+
ruby-2.7.1-rails-5.2:
|
|
65
65
|
docker:
|
|
66
|
-
- image: circleci/ruby:2.
|
|
66
|
+
- image: circleci/ruby:2.7.1
|
|
67
67
|
environment:
|
|
68
|
-
BUNDLE_GEMFILE: Gemfile.rails-5.
|
|
68
|
+
BUNDLE_GEMFILE: Gemfile.rails-5.2
|
|
69
69
|
working_directory: "~/stitches"
|
|
70
70
|
steps:
|
|
71
71
|
- checkout
|
|
@@ -79,15 +79,15 @@ jobs:
|
|
|
79
79
|
fi
|
|
80
80
|
- run:
|
|
81
81
|
name: Notify Pager Duty
|
|
82
|
-
command: bundle exec y-notify "#
|
|
82
|
+
command: bundle exec y-notify "#devex-alerts"
|
|
83
83
|
when: on_fail
|
|
84
84
|
- store_test_results:
|
|
85
85
|
path: "/tmp/test-results"
|
|
86
|
-
ruby-2.
|
|
86
|
+
ruby-2.6.6-rails-5.2:
|
|
87
87
|
docker:
|
|
88
|
-
- image: circleci/ruby:2.
|
|
88
|
+
- image: circleci/ruby:2.6.6
|
|
89
89
|
environment:
|
|
90
|
-
BUNDLE_GEMFILE: Gemfile.rails-5.
|
|
90
|
+
BUNDLE_GEMFILE: Gemfile.rails-5.2
|
|
91
91
|
working_directory: "~/stitches"
|
|
92
92
|
steps:
|
|
93
93
|
- checkout
|
|
@@ -101,7 +101,7 @@ jobs:
|
|
|
101
101
|
fi
|
|
102
102
|
- run:
|
|
103
103
|
name: Notify Pager Duty
|
|
104
|
-
command: bundle exec y-notify "#
|
|
104
|
+
command: bundle exec y-notify "#devex-alerts"
|
|
105
105
|
when: on_fail
|
|
106
106
|
- store_test_results:
|
|
107
107
|
path: "/tmp/test-results"
|
|
@@ -112,31 +112,31 @@ workflows:
|
|
|
112
112
|
- release:
|
|
113
113
|
context: org-global
|
|
114
114
|
requires:
|
|
115
|
-
- ruby-2.
|
|
116
|
-
- ruby-2.
|
|
117
|
-
- ruby-2.
|
|
118
|
-
- ruby-2.
|
|
115
|
+
- ruby-2.7.1-rails-6.0
|
|
116
|
+
- ruby-2.6.6-rails-6.0
|
|
117
|
+
- ruby-2.7.1-rails-5.2
|
|
118
|
+
- ruby-2.6.6-rails-5.2
|
|
119
119
|
filters:
|
|
120
120
|
tags:
|
|
121
|
-
only: /^[0-9]+\.[0-9]+\.[0-9](
|
|
121
|
+
only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?(RC|rc)[-\.]?\d*)?$/
|
|
122
122
|
branches:
|
|
123
123
|
ignore: /.*/
|
|
124
|
-
- ruby-2.
|
|
124
|
+
- ruby-2.7.1-rails-6.0:
|
|
125
125
|
context: org-global
|
|
126
126
|
filters:
|
|
127
127
|
tags:
|
|
128
128
|
only: &1 /.*/
|
|
129
|
-
- ruby-2.
|
|
129
|
+
- ruby-2.6.6-rails-6.0:
|
|
130
130
|
context: org-global
|
|
131
131
|
filters:
|
|
132
132
|
tags:
|
|
133
133
|
only: *1
|
|
134
|
-
- ruby-2.
|
|
134
|
+
- ruby-2.7.1-rails-5.2:
|
|
135
135
|
context: org-global
|
|
136
136
|
filters:
|
|
137
137
|
tags:
|
|
138
138
|
only: *1
|
|
139
|
-
- ruby-2.
|
|
139
|
+
- ruby-2.6.6-rails-5.2:
|
|
140
140
|
context: org-global
|
|
141
141
|
filters:
|
|
142
142
|
tags:
|
|
@@ -150,11 +150,11 @@ workflows:
|
|
|
150
150
|
only:
|
|
151
151
|
- master
|
|
152
152
|
jobs:
|
|
153
|
-
- ruby-2.
|
|
153
|
+
- ruby-2.7.1-rails-6.0:
|
|
154
154
|
context: org-global
|
|
155
|
-
- ruby-2.
|
|
155
|
+
- ruby-2.6.6-rails-6.0:
|
|
156
156
|
context: org-global
|
|
157
|
-
- ruby-2.
|
|
157
|
+
- ruby-2.7.1-rails-5.2:
|
|
158
158
|
context: org-global
|
|
159
|
-
- ruby-2.
|
|
159
|
+
- ruby-2.6.6-rails-5.2:
|
|
160
160
|
context: org-global
|
|
File without changes
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.7.1
|
data/.travis.yml
CHANGED
data/Gemfile.rails-5.1
CHANGED
data/Gemfile.rails-5.2
CHANGED
data/Gemfile.rails-6.0
ADDED
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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.
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
80
|
-
you, so there's zero need to use `respond_to` and friends.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
about it in your code.
|
|
84
|
-
|
|
85
|
-
routed here.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
22
|
+
Bundler.with_clean_env do
|
|
23
|
+
run "bundle install"
|
|
24
|
+
end
|
|
24
25
|
generate "apitome:install"
|
|
25
26
|
generate "rspec:install"
|
|
26
27
|
|
data/lib/stitches/api_key.rb
CHANGED
|
@@ -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.application.class.parent.to_s
|
|
26
|
-
end
|
|
27
|
-
|
|
28
23
|
protected
|
|
29
24
|
|
|
30
25
|
def do_call(env)
|
|
@@ -32,35 +27,40 @@ 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
|
-
|
|
36
|
+
unauthorized_response("key invalid")
|
|
49
37
|
end
|
|
50
38
|
else
|
|
51
|
-
|
|
39
|
+
unauthorized_response("bad authorization type")
|
|
52
40
|
end
|
|
53
41
|
else
|
|
54
|
-
|
|
42
|
+
unauthorized_response("no authorization header")
|
|
55
43
|
end
|
|
56
44
|
end
|
|
57
45
|
|
|
58
46
|
private
|
|
59
47
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
# TODO: (jdlubrano)
|
|
49
|
+
# Once Rails 5 support is no longer necessary, we can simply call
|
|
50
|
+
# Rails.application.class.module_parent. The module_parent method
|
|
51
|
+
# does not exist in Rails <= 5, though, so we need to gracefully fallback
|
|
52
|
+
# Rails.application.class.parent for Rails versions predating Rails 6.0.0.
|
|
53
|
+
def rails_app_module
|
|
54
|
+
application_class = Rails.application.class
|
|
55
|
+
parent = application_class.try(:module_parent) || application_class.parent
|
|
56
|
+
parent.to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
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
|
|
64
64
|
end
|
|
65
65
|
|
|
66
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
|
|
@@ -123,14 +123,22 @@ feature "general API stuff" do
|
|
|
123
123
|
failure_message do
|
|
124
124
|
correct_code,_ = evaluate_response(response)
|
|
125
125
|
if correct_code
|
|
126
|
-
"Expected WWW-Authenticate header to be 'CustomKeyAuth realm=#{
|
|
126
|
+
"Expected WWW-Authenticate header to be 'CustomKeyAuth realm=#{realm}', but was #{response['WWW-Authenticate']}"
|
|
127
127
|
else
|
|
128
128
|
"Expected response to be 401, but was #{response.response_code}"
|
|
129
129
|
end
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def realm
|
|
133
|
+
<% if ::Rails::VERSION::MAJOR >= 6 -%>
|
|
134
|
+
Rails.application.class.module_parent.to_s
|
|
135
|
+
<% else %>
|
|
136
|
+
Rails.application.class.parent.to_s
|
|
137
|
+
<% end %>
|
|
138
|
+
end
|
|
139
|
+
|
|
132
140
|
def evaluate_response(response)
|
|
133
|
-
[response.response_code == 401, response.headers["WWW-Authenticate"] == "CustomKeyAuth realm=#{
|
|
141
|
+
[response.response_code == 401, response.headers["WWW-Authenticate"] == "CustomKeyAuth realm=#{realm}"]
|
|
134
142
|
end
|
|
135
143
|
end
|
|
136
144
|
end
|
data/lib/stitches/railtie.rb
CHANGED
|
@@ -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
|
|
@@ -16,16 +16,17 @@ module Stitches
|
|
|
16
16
|
if accept =~ %r{application/json} && accept =~ %r{version=\d+}
|
|
17
17
|
@app.call(env)
|
|
18
18
|
else
|
|
19
|
-
|
|
19
|
+
not_acceptable_response(accept)
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
def not_acceptable_response(accept_header)
|
|
26
|
+
status = 406
|
|
27
|
+
body = "Not Acceptable - '#{accept_header}' didn't have the right mime type or version number. We only accept application/json with a version"
|
|
28
|
+
header = { "WWW-Authenticate" => accept_header }
|
|
29
|
+
Rack::Response.new(body, status, header).finish
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
end
|
data/lib/stitches/version.rb
CHANGED
data/lib/stitches_norailtie.rb
CHANGED
|
@@ -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'
|
data/owners.json
CHANGED
|
@@ -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
|
data/spec/api_key_spec.rb
CHANGED
|
@@ -15,10 +15,8 @@ end
|
|
|
15
15
|
|
|
16
16
|
describe Stitches::ApiKey do
|
|
17
17
|
let(:app) { double("rack app") }
|
|
18
|
-
let(:
|
|
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(:
|
|
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
|
-
|
|
36
|
+
status, _headers, _body = @response
|
|
37
|
+
expect(status).to eq(401)
|
|
38
38
|
end
|
|
39
39
|
it "sets the proper header" do
|
|
40
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(:
|
|
179
|
+
let(:api_client) { nil }
|
|
179
180
|
|
|
180
181
|
it_behaves_like "an unauthorized response" do
|
|
181
182
|
let(:expected_body) { "Unauthorized - key invalid" }
|
data/spec/configuration_spec.rb
CHANGED
|
@@ -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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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 "
|
|
114
|
-
|
|
115
|
-
config.
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -133,7 +135,6 @@ describe Stitches::ValidMimeType do
|
|
|
133
135
|
context "unacceptable responses" do
|
|
134
136
|
before do
|
|
135
137
|
@response = middleware.call(env)
|
|
136
|
-
@response.finish
|
|
137
138
|
end
|
|
138
139
|
context "no header" do
|
|
139
140
|
let(:env) {
|
data/stitches.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stitches
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stitch Fix Engineering
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date:
|
|
14
|
+
date: 2020-07-27 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
|
|
@@ -107,8 +122,8 @@ files:
|
|
|
107
122
|
- Gemfile.rails-5.0
|
|
108
123
|
- Gemfile.rails-5.1
|
|
109
124
|
- Gemfile.rails-5.2
|
|
125
|
+
- Gemfile.rails-6.0
|
|
110
126
|
- LICENSE.txt
|
|
111
|
-
- PULL_REQUEST_TEMPLATE.md
|
|
112
127
|
- README.md
|
|
113
128
|
- Rakefile
|
|
114
129
|
- build-matrix.json
|
|
@@ -116,6 +131,7 @@ files:
|
|
|
116
131
|
- lib/stitches/add_deprecation_generator.rb
|
|
117
132
|
- lib/stitches/add_enabled_to_api_clients_generator.rb
|
|
118
133
|
- lib/stitches/allowlist_middleware.rb
|
|
134
|
+
- lib/stitches/api_client_access_wrapper.rb
|
|
119
135
|
- lib/stitches/api_generator.rb
|
|
120
136
|
- lib/stitches/api_key.rb
|
|
121
137
|
- lib/stitches/api_version_constraint.rb
|
|
@@ -146,12 +162,11 @@ files:
|
|
|
146
162
|
- lib/stitches/spec/have_api_error.rb
|
|
147
163
|
- lib/stitches/spec/show_deprecation.rb
|
|
148
164
|
- lib/stitches/spec/test_headers.rb
|
|
149
|
-
- lib/stitches/update_configuration_generator.rb
|
|
150
165
|
- lib/stitches/valid_mime_type.rb
|
|
151
166
|
- lib/stitches/version.rb
|
|
152
|
-
- lib/stitches/whitelisting_middleware.rb
|
|
153
167
|
- lib/stitches_norailtie.rb
|
|
154
168
|
- owners.json
|
|
169
|
+
- spec/api_client_access_wrapper_spec.rb
|
|
155
170
|
- spec/api_key_spec.rb
|
|
156
171
|
- spec/api_version_constraint_spec.rb
|
|
157
172
|
- spec/configuration_spec.rb
|
|
@@ -184,11 +199,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
184
199
|
- !ruby/object:Gem::Version
|
|
185
200
|
version: '0'
|
|
186
201
|
requirements: []
|
|
187
|
-
rubygems_version: 3.
|
|
202
|
+
rubygems_version: 3.1.2
|
|
188
203
|
signing_key:
|
|
189
204
|
specification_version: 4
|
|
190
205
|
summary: You'll be in stitches at how easy it is to create a service at Stitch Fix
|
|
191
206
|
test_files:
|
|
207
|
+
- spec/api_client_access_wrapper_spec.rb
|
|
192
208
|
- spec/api_key_spec.rb
|
|
193
209
|
- spec/api_version_constraint_spec.rb
|
|
194
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
|