stitches 3.8.3 → 4.0.0.RC1

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: 67774c77c5427de523a5462be693b924f7f10be2411910bfc9e97f2dca1df2ed
4
- data.tar.gz: 2029ae0229fab1dacd561039047d4b26f5bbb3282634fc2ab7d88d964fdc82b9
3
+ metadata.gz: d181adb5b8ed9642468e2a8c75ab5ed2cbb01756458885410c8538c0b0a51890
4
+ data.tar.gz: 95a4105d911e4e4ebbf927d2bf87efb8bc6dfb1d2537e4f0ebc42caf8569c44d
5
5
  SHA512:
6
- metadata.gz: e06dc6363f827519cbae0c3b6ab7e7f475b4fd4425ad810bf769d571c8b9d2cd2b3208cfc82c14339fc3634827284c8a59f7cf30e1ddebf38b3426cb9229ea43
7
- data.tar.gz: 9e561f3fa139a3cb1e043cf21a114b9f155997a886454aadefe194c5865f28c45e0e9069206810d7c313f52ebe0ca3768579942de76cc11adb241be3d5d22a4e
6
+ metadata.gz: 442b3c89184c500c7036bb06a355c0df478ea16089777d920aefc62c94dedb37ad0be40a4a46dc558509148b0f08feb335cece9e8e79ac732482977876931d99
7
+ data.tar.gz: aeaf701f19b4e8962227cccd7421f3452bf7e2dde77bfa2e7ecce735883adade7903a3392009d351e48258d2b6f231f36d36190a8e65c184e8ad5b9681d94f7f
@@ -5,7 +5,7 @@ version: 2
5
5
  jobs:
6
6
  release:
7
7
  docker:
8
- - image: circleci/ruby:2.7.0
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.0-rails-6.0:
20
+ ruby-2.7.1-rails-6.0:
21
21
  docker:
22
- - image: circleci/ruby:2.7.0
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.5-rails-6.0:
42
+ ruby-2.6.6-rails-6.0:
43
43
  docker:
44
- - image: circleci/ruby:2.6.5
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.0-rails-5.2:
64
+ ruby-2.7.1-rails-5.2:
65
65
  docker:
66
- - image: circleci/ruby:2.7.0
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.5-rails-5.2:
86
+ ruby-2.6.6-rails-5.2:
87
87
  docker:
88
- - image: circleci/ruby:2.6.5
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.0-rails-6.0
116
- - ruby-2.6.5-rails-6.0
117
- - ruby-2.7.0-rails-5.2
118
- - ruby-2.6.5-rails-5.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]+(\.?RC[-\.]?\d*)?$/
121
+ only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?(RC|rc)[-\.]?\d*)?$/
122
122
  branches:
123
123
  ignore: /.*/
124
- - ruby-2.7.0-rails-6.0:
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.5-rails-6.0:
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.0-rails-5.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.6.5-rails-5.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.7.0-rails-6.0:
153
+ - ruby-2.7.1-rails-6.0:
154
154
  context: org-global
155
- - ruby-2.6.5-rails-6.0:
155
+ - ruby-2.6.6-rails-6.0:
156
156
  context: org-global
157
- - ruby-2.7.0-rails-5.2:
157
+ - ruby-2.7.1-rails-5.2:
158
158
  context: org-global
159
- - ruby-2.6.5-rails-5.2:
159
+ - ruby-2.6.6-rails-5.2:
160
160
  context: org-global
@@ -1 +1 @@
1
- 2.7.0
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
- * 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,44 @@ 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)
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. 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.
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. Do not do
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
@@ -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
@@ -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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Stitches
2
- VERSION = "3.8.3"
4
+ VERSION = '4.0.0.RC1'
3
5
  end
@@ -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,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(: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") }
@@ -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(api_clients.first.id)
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(api_clients.first)
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(:api_clients) { [] }
179
+ let(:api_client) { nil }
181
180
 
182
181
  it_behaves_like "an unauthorized response" do
183
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,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") }
@@ -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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stitches
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.3
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-03-04 00:00:00.000000000 Z
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: '0'
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