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 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