stitches 3.8.3 → 4.0.0.RC1
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 +22 -22
- data/.ruby-version +1 -1
- data/README.md +38 -26
- data/lib/stitches/allowlist_middleware.rb +0 -1
- data/lib/stitches/api_client_access_wrapper.rb +42 -0
- data/lib/stitches/api_key.rb +1 -8
- data/lib/stitches/configuration.rb +35 -1
- data/lib/stitches/generator_files/config/initializers/stitches.rb +10 -0
- data/lib/stitches/railtie.rb +2 -0
- data/lib/stitches/version.rb +3 -1
- data/spec/api_client_access_wrapper_spec.rb +52 -0
- data/spec/api_key_spec.rb +7 -8
- data/spec/configuration_spec.rb +45 -0
- data/stitches.gemspec +1 -0
- metadata +21 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d181adb5b8ed9642468e2a8c75ab5ed2cbb01756458885410c8538c0b0a51890
|
4
|
+
data.tar.gz: 95a4105d911e4e4ebbf927d2bf87efb8bc6dfb1d2537e4f0ebc42caf8569c44d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 442b3c89184c500c7036bb06a355c0df478ea16089777d920aefc62c94dedb37ad0be40a4a46dc558509148b0f08feb335cece9e8e79ac732482977876931d99
|
7
|
+
data.tar.gz: aeaf701f19b4e8962227cccd7421f3452bf7e2dde77bfa2e7ecce735883adade7903a3392009d351e48258d2b6f231f36d36190a8e65c184e8ad5b9681d94f7f
|
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.7.
|
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,9 +17,9 @@ jobs:
|
|
17
17
|
- run:
|
18
18
|
name: Build/release gem to artifactory
|
19
19
|
command: bundle exec rake push_artifactory
|
20
|
-
ruby-2.7.
|
20
|
+
ruby-2.7.1-rails-6.0:
|
21
21
|
docker:
|
22
|
-
- image: circleci/ruby:2.7.
|
22
|
+
- image: circleci/ruby:2.7.1
|
23
23
|
environment:
|
24
24
|
BUNDLE_GEMFILE: Gemfile.rails-6.0
|
25
25
|
working_directory: "~/stitches"
|
@@ -39,9 +39,9 @@ jobs:
|
|
39
39
|
when: on_fail
|
40
40
|
- store_test_results:
|
41
41
|
path: "/tmp/test-results"
|
42
|
-
ruby-2.6.
|
42
|
+
ruby-2.6.6-rails-6.0:
|
43
43
|
docker:
|
44
|
-
- image: circleci/ruby:2.6.
|
44
|
+
- image: circleci/ruby:2.6.6
|
45
45
|
environment:
|
46
46
|
BUNDLE_GEMFILE: Gemfile.rails-6.0
|
47
47
|
working_directory: "~/stitches"
|
@@ -61,9 +61,9 @@ jobs:
|
|
61
61
|
when: on_fail
|
62
62
|
- store_test_results:
|
63
63
|
path: "/tmp/test-results"
|
64
|
-
ruby-2.7.
|
64
|
+
ruby-2.7.1-rails-5.2:
|
65
65
|
docker:
|
66
|
-
- image: circleci/ruby:2.7.
|
66
|
+
- image: circleci/ruby:2.7.1
|
67
67
|
environment:
|
68
68
|
BUNDLE_GEMFILE: Gemfile.rails-5.2
|
69
69
|
working_directory: "~/stitches"
|
@@ -83,9 +83,9 @@ jobs:
|
|
83
83
|
when: on_fail
|
84
84
|
- store_test_results:
|
85
85
|
path: "/tmp/test-results"
|
86
|
-
ruby-2.6.
|
86
|
+
ruby-2.6.6-rails-5.2:
|
87
87
|
docker:
|
88
|
-
- image: circleci/ruby:2.6.
|
88
|
+
- image: circleci/ruby:2.6.6
|
89
89
|
environment:
|
90
90
|
BUNDLE_GEMFILE: Gemfile.rails-5.2
|
91
91
|
working_directory: "~/stitches"
|
@@ -112,31 +112,31 @@ workflows:
|
|
112
112
|
- release:
|
113
113
|
context: org-global
|
114
114
|
requires:
|
115
|
-
- ruby-2.7.
|
116
|
-
- ruby-2.6.
|
117
|
-
- ruby-2.7.
|
118
|
-
- ruby-2.6.
|
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]+(\.?RC[-\.]?\d*)?$/
|
121
|
+
only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?(RC|rc)[-\.]?\d*)?$/
|
122
122
|
branches:
|
123
123
|
ignore: /.*/
|
124
|
-
- ruby-2.7.
|
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.6.
|
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.7.
|
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.6.
|
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.7.
|
153
|
+
- ruby-2.7.1-rails-6.0:
|
154
154
|
context: org-global
|
155
|
-
- ruby-2.6.
|
155
|
+
- ruby-2.6.6-rails-6.0:
|
156
156
|
context: org-global
|
157
|
-
- ruby-2.7.
|
157
|
+
- ruby-2.7.1-rails-5.2:
|
158
158
|
context: org-global
|
159
|
-
- ruby-2.6.
|
159
|
+
- ruby-2.6.6-rails-5.2:
|
160
160
|
context: org-global
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.7.
|
1
|
+
2.7.1
|
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,44 @@ 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
|
-
|
106
|
-
|
118
|
+
- Stitches provides [testing support](https://github.com/stitchfix/stitches/wiki/Testing)
|
107
119
|
|
108
120
|
## Developing
|
109
121
|
|
110
|
-
Although `Stitches.configuration` is global, do not depend directly on that in your logic.
|
122
|
+
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
123
|
|
112
124
|
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.
|
125
|
+
failing with a successful result, we have to make sure that the various `inject_into_file` calls are actually working. Do not do
|
114
126
|
any fancy refactors here, just keep it up to date.
|
115
127
|
|
116
128
|
---
|
@@ -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
|
data/lib/stitches/api_key.rb
CHANGED
@@ -27,14 +27,7 @@ module Stitches
|
|
27
27
|
if authorization
|
28
28
|
if authorization =~ /#{@configuration.custom_http_auth_scheme}\s+key=(.*)\s*$/
|
29
29
|
key = $1
|
30
|
-
|
31
|
-
if ApiClient.column_names.include?("enabled")
|
32
|
-
client = ApiClient.where(key: key, enabled: true).first
|
33
|
-
else
|
34
|
-
ActiveSupport::Deprecation.warn('api_keys is missing "enabled" column. Run "rails g stitches:add_enabled_to_api_clients"')
|
35
|
-
client = ApiClient.where(key: key).first
|
36
|
-
end
|
37
|
-
|
30
|
+
client = Stitches::ApiClientAccessWrapper.fetch_for_key(key)
|
38
31
|
if client.present?
|
39
32
|
env[@configuration.env_var_to_hold_api_client_primary_key] = client.id
|
40
33
|
env[@configuration.env_var_to_hold_api_client] = client
|
@@ -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.
|
@@ -39,7 +41,7 @@ class Stitches::Configuration
|
|
39
41
|
@custom_http_auth_scheme = NonNullString.new("custom_http_auth_scheme",new_custom_http_auth_scheme)
|
40
42
|
end
|
41
43
|
|
42
|
-
# The name of the environment variable that the ApiKey middleware should use to
|
44
|
+
# The name of the environment variable that the ApiKey middleware should use to
|
43
45
|
# place the primary key of the authenticated ApiKey. For example, if a user provides
|
44
46
|
# the api key 1234-1234-1234-1234, and that maps to the primary key 42 in your database,
|
45
47
|
# the environment will contain "42" in the key provided here.
|
@@ -59,8 +61,40 @@ class Stitches::Configuration
|
|
59
61
|
@env_var_to_hold_api_client= NonNullString.new("env_var_to_hold_api_client",new_env_var_to_hold_api_client)
|
60
62
|
end
|
61
63
|
|
64
|
+
def max_cache_ttl
|
65
|
+
@max_cache_ttl.to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
def max_cache_ttl=(new_max_cache_ttl)
|
69
|
+
@max_cache_ttl = NonNullInteger.new("max_cache_ttl", new_max_cache_ttl)
|
70
|
+
end
|
71
|
+
|
72
|
+
def max_cache_size
|
73
|
+
@max_cache_size.to_i
|
74
|
+
end
|
75
|
+
|
76
|
+
def max_cache_size=(new_max_cache_size)
|
77
|
+
@max_cache_size = NonNullInteger.new("max_cache_size", new_max_cache_size)
|
78
|
+
end
|
79
|
+
|
62
80
|
private
|
63
81
|
|
82
|
+
class NonNullInteger
|
83
|
+
def initialize(name, value)
|
84
|
+
unless value.is_a?(Integer)
|
85
|
+
raise "#{name} must be an Integer, not a #{value.class}"
|
86
|
+
end
|
87
|
+
|
88
|
+
@value = value
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_i
|
92
|
+
@value
|
93
|
+
end
|
94
|
+
|
95
|
+
alias to_integer to_i
|
96
|
+
end
|
97
|
+
|
64
98
|
class NonNullString
|
65
99
|
def initialize(name,string)
|
66
100
|
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
|
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
|
data/lib/stitches/version.rb
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,7 +25,8 @@ 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") }
|
@@ -158,11 +157,11 @@ describe Stitches::ApiKey do
|
|
158
157
|
end
|
159
158
|
|
160
159
|
it "sets the api_client's ID in the environment" do
|
161
|
-
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)
|
162
161
|
end
|
163
162
|
|
164
163
|
it "sets the api_client itself in the environment" do
|
165
|
-
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)
|
166
165
|
end
|
167
166
|
end
|
168
167
|
|
@@ -177,7 +176,7 @@ describe Stitches::ApiKey do
|
|
177
176
|
"HTTP_AUTHORIZATION" => "MyAwesomeInternalScheme key=foobar",
|
178
177
|
}
|
179
178
|
}
|
180
|
-
let(:
|
179
|
+
let(:api_client) { nil }
|
181
180
|
|
182
181
|
it_behaves_like "an unauthorized response" do
|
183
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,6 +116,37 @@ describe Stitches::Configuration do
|
|
102
116
|
}.not_to raise_error
|
103
117
|
end
|
104
118
|
end
|
119
|
+
|
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/)
|
126
|
+
end
|
127
|
+
|
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/)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
105
150
|
context "deprecated options we want to support for backwards compatibility" do
|
106
151
|
|
107
152
|
let(:logger) { double("logger") }
|
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.RC1
|
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: 2020-
|
14
|
+
date: 2020-07-22 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
|
@@ -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
|
@@ -153,6 +168,7 @@ files:
|
|
153
168
|
- lib/stitches/whitelisting_middleware.rb
|
154
169
|
- lib/stitches_norailtie.rb
|
155
170
|
- owners.json
|
171
|
+
- spec/api_client_access_wrapper_spec.rb
|
156
172
|
- spec/api_key_spec.rb
|
157
173
|
- spec/api_version_constraint_spec.rb
|
158
174
|
- spec/configuration_spec.rb
|
@@ -181,15 +197,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
181
197
|
version: '0'
|
182
198
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
199
|
requirements:
|
184
|
-
- - "
|
200
|
+
- - ">"
|
185
201
|
- !ruby/object:Gem::Version
|
186
|
-
version:
|
202
|
+
version: 1.3.1
|
187
203
|
requirements: []
|
188
204
|
rubygems_version: 3.1.2
|
189
205
|
signing_key:
|
190
206
|
specification_version: 4
|
191
207
|
summary: You'll be in stitches at how easy it is to create a service at Stitch Fix
|
192
208
|
test_files:
|
209
|
+
- spec/api_client_access_wrapper_spec.rb
|
193
210
|
- spec/api_key_spec.rb
|
194
211
|
- spec/api_version_constraint_spec.rb
|
195
212
|
- spec/configuration_spec.rb
|