stitches 3.7.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: 5fa18e703222c9f9f2cc2b750cbf06a1bf9ddac8d473e6c2775d3d0cd840e59e
4
- data.tar.gz: 550563efbfc95a84ff7c9375cbd4cd6d45eff8621370780f90760ddecc4afa3e
3
+ metadata.gz: d181adb5b8ed9642468e2a8c75ab5ed2cbb01756458885410c8538c0b0a51890
4
+ data.tar.gz: 95a4105d911e4e4ebbf927d2bf87efb8bc6dfb1d2537e4f0ebc42caf8569c44d
5
5
  SHA512:
6
- metadata.gz: 0d81db63050704736cc9aaaebc3ccc0f06bcb271a36b734c195f9b5298121c18fd43c529e32ec2ba2dcf2e76c8dda411fc245d8229a4a2df21889de87dded7d7
7
- data.tar.gz: cd636756307dd9395860278f80f9fea1638ceef28105d91cc319c80db74de58809e7e6e396f1e706b123705c7905a46d0a5e3655c7c84cd077fe6eab9808c577
6
+ metadata.gz: 442b3c89184c500c7036bb06a355c0df478ea16089777d920aefc62c94dedb37ad0be40a4a46dc558509148b0f08feb335cece9e8e79ac732482977876931d99
7
+ data.tar.gz: aeaf701f19b4e8962227cccd7421f3452bf7e2dde77bfa2e7ecce735883adade7903a3392009d351e48258d2b6f231f36d36190a8e65c184e8ad5b9681d94f7f
@@ -5,9 +5,10 @@ version: 2
5
5
  jobs:
6
6
  release:
7
7
  docker:
8
- - image: circleci/ruby:2.6.3
8
+ - image: circleci/ruby:2.7.1
9
9
  steps:
10
10
  - checkout
11
+ - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
11
12
  - run: bundle install --full-index
12
13
  - run:
13
14
  name: Artifactory login
@@ -16,14 +17,15 @@ jobs:
16
17
  - run:
17
18
  name: Build/release gem to artifactory
18
19
  command: bundle exec rake push_artifactory
19
- ruby-2.6.3-rails-5.2:
20
+ ruby-2.7.1-rails-6.0:
20
21
  docker:
21
- - image: circleci/ruby:2.6.3
22
+ - image: circleci/ruby:2.7.1
22
23
  environment:
23
- BUNDLE_GEMFILE: Gemfile.rails-5.2
24
+ BUNDLE_GEMFILE: Gemfile.rails-6.0
24
25
  working_directory: "~/stitches"
25
26
  steps:
26
27
  - checkout
28
+ - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
27
29
  - run: bundle install --full-index
28
30
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
29
31
  --format=doc
@@ -33,18 +35,19 @@ jobs:
33
35
  fi
34
36
  - run:
35
37
  name: Notify Pager Duty
36
- command: bundle exec y-notify "#eng-platform"
38
+ command: bundle exec y-notify "#devex-alerts"
37
39
  when: on_fail
38
40
  - store_test_results:
39
41
  path: "/tmp/test-results"
40
- ruby-2.5.5-rails-5.2:
42
+ ruby-2.6.6-rails-6.0:
41
43
  docker:
42
- - image: circleci/ruby:2.5.5
44
+ - image: circleci/ruby:2.6.6
43
45
  environment:
44
- BUNDLE_GEMFILE: Gemfile.rails-5.2
46
+ BUNDLE_GEMFILE: Gemfile.rails-6.0
45
47
  working_directory: "~/stitches"
46
48
  steps:
47
49
  - checkout
50
+ - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
48
51
  - run: bundle install --full-index
49
52
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
50
53
  --format=doc
@@ -54,18 +57,19 @@ jobs:
54
57
  fi
55
58
  - run:
56
59
  name: Notify Pager Duty
57
- command: bundle exec y-notify "#eng-platform"
60
+ command: bundle exec y-notify "#devex-alerts"
58
61
  when: on_fail
59
62
  - store_test_results:
60
63
  path: "/tmp/test-results"
61
- ruby-2.6.3-rails-5.1:
64
+ ruby-2.7.1-rails-5.2:
62
65
  docker:
63
- - image: circleci/ruby:2.6.3
66
+ - image: circleci/ruby:2.7.1
64
67
  environment:
65
- BUNDLE_GEMFILE: Gemfile.rails-5.1
68
+ BUNDLE_GEMFILE: Gemfile.rails-5.2
66
69
  working_directory: "~/stitches"
67
70
  steps:
68
71
  - checkout
72
+ - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
69
73
  - run: bundle install --full-index
70
74
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
71
75
  --format=doc
@@ -75,18 +79,19 @@ jobs:
75
79
  fi
76
80
  - run:
77
81
  name: Notify Pager Duty
78
- command: bundle exec y-notify "#eng-platform"
82
+ command: bundle exec y-notify "#devex-alerts"
79
83
  when: on_fail
80
84
  - store_test_results:
81
85
  path: "/tmp/test-results"
82
- ruby-2.5.5-rails-5.1:
86
+ ruby-2.6.6-rails-5.2:
83
87
  docker:
84
- - image: circleci/ruby:2.5.5
88
+ - image: circleci/ruby:2.6.6
85
89
  environment:
86
- BUNDLE_GEMFILE: Gemfile.rails-5.1
90
+ BUNDLE_GEMFILE: Gemfile.rails-5.2
87
91
  working_directory: "~/stitches"
88
92
  steps:
89
93
  - checkout
94
+ - run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
90
95
  - run: bundle install --full-index
91
96
  - run: bundle exec rspec --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
92
97
  --format=doc
@@ -96,7 +101,7 @@ jobs:
96
101
  fi
97
102
  - run:
98
103
  name: Notify Pager Duty
99
- command: bundle exec y-notify "#eng-platform"
104
+ command: bundle exec y-notify "#devex-alerts"
100
105
  when: on_fail
101
106
  - store_test_results:
102
107
  path: "/tmp/test-results"
@@ -107,31 +112,31 @@ workflows:
107
112
  - release:
108
113
  context: org-global
109
114
  requires:
110
- - ruby-2.6.3-rails-5.2
111
- - ruby-2.5.5-rails-5.2
112
- - ruby-2.6.3-rails-5.1
113
- - ruby-2.5.5-rails-5.1
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
114
119
  filters:
115
120
  tags:
116
- only: /^[0-9]+\.[0-9]+\.[0-9](\.RC\d*)?$/
121
+ only: /^[0-9]+\.[0-9]+\.[0-9]+(\.?(RC|rc)[-\.]?\d*)?$/
117
122
  branches:
118
123
  ignore: /.*/
119
- - ruby-2.6.3-rails-5.2:
124
+ - ruby-2.7.1-rails-6.0:
120
125
  context: org-global
121
126
  filters:
122
127
  tags:
123
128
  only: &1 /.*/
124
- - ruby-2.5.5-rails-5.2:
129
+ - ruby-2.6.6-rails-6.0:
125
130
  context: org-global
126
131
  filters:
127
132
  tags:
128
133
  only: *1
129
- - ruby-2.6.3-rails-5.1:
134
+ - ruby-2.7.1-rails-5.2:
130
135
  context: org-global
131
136
  filters:
132
137
  tags:
133
138
  only: *1
134
- - ruby-2.5.5-rails-5.1:
139
+ - ruby-2.6.6-rails-5.2:
135
140
  context: org-global
136
141
  filters:
137
142
  tags:
@@ -145,11 +150,11 @@ workflows:
145
150
  only:
146
151
  - master
147
152
  jobs:
148
- - ruby-2.6.3-rails-5.2:
153
+ - ruby-2.7.1-rails-6.0:
149
154
  context: org-global
150
- - ruby-2.5.5-rails-5.2:
155
+ - ruby-2.6.6-rails-6.0:
151
156
  context: org-global
152
- - ruby-2.6.3-rails-5.1:
157
+ - ruby-2.7.1-rails-5.2:
153
158
  context: org-global
154
- - ruby-2.5.5-rails-5.1:
159
+ - ruby-2.6.6-rails-5.2:
155
160
  context: org-global
@@ -8,4 +8,4 @@
8
8
  # This file uses the GitHub CODEOWNERS convention to assign PR reviewers:
9
9
  # https://help.github.com/articles/about-codeowners/
10
10
 
11
- * @brettfishman @bwebster
11
+ * @brettfishman @bwebster @stitchfix/devex
@@ -0,0 +1,7 @@
1
+ ## Problem
2
+
3
+ «Brief overview of the problem»
4
+
5
+ ## Solution
6
+
7
+ «Brief description of how you solved the problem»
data/.gitignore CHANGED
@@ -9,4 +9,5 @@ config/database.yml
9
9
  .jhw-cache
10
10
  **.orig
11
11
  Gemfile.lock
12
+ Gemfile.*.lock
12
13
  .projections.json
@@ -1 +1 @@
1
- 2.5.3
1
+ 2.7.1
@@ -1,7 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.4.4
4
- - 2.5.1
3
+ - 2.6
4
+ - 2.7
5
5
  - ruby-head
6
6
  notifications:
7
7
  email: false
8
+ jobs:
9
+ allow_failures:
10
+ - rvm: ruby-head
@@ -1,7 +1,6 @@
1
1
  # DO NOT MODIFY - this is managed by Git Reduce in goro
2
2
  #
3
- source 'https://gem.fury.io/me/'
4
- source 'https://www.rubygems.org'
3
+ source 'https://stitchfix01.jfrog.io/stitchfix01/api/gems/eng-gems/'
5
4
 
6
5
  gemspec
7
6
 
@@ -1,7 +1,6 @@
1
1
  # DO NOT MODIFY - this is managed by Git Reduce in goro
2
2
  #
3
- source 'https://gem.fury.io/me/'
4
- source 'https://www.rubygems.org'
3
+ source 'https://stitchfix01.jfrog.io/stitchfix01/api/gems/eng-gems/'
5
4
 
6
5
  gemspec
7
6
 
@@ -0,0 +1,7 @@
1
+ # DO NOT MODIFY - this is managed by Git Reduce in goro
2
+ #
3
+ source 'https://stitchfix01.jfrog.io/stitchfix01/api/gems/eng-gems/'
4
+
5
+ gemspec
6
+
7
+ gem 'rails', '~> 6.0.0'
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
 
@@ -29,22 +29,32 @@ bundle install
29
29
  Then, set it up:
30
30
 
31
31
  ```
32
- > bin/rails generate rspec:install
33
- > bin/rails generate apitome:install
34
32
  > bin/rails generate stitches:api
35
33
  > bundle exec rake db:migrate
36
34
  ```
37
35
 
38
36
  ### Upgrading from an older version
39
37
 
40
- * 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
41
- `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:
42
51
 
43
52
  ```
44
53
  > bin/rails generate stitches:add_enabled_to_api_clients
45
54
  > bin/rails generate stitches:add_deprecation
46
55
  ```
47
- * 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:
48
58
 
49
59
  ```
50
60
  > bin/rails generate stitches:add_deprecation
@@ -61,8 +71,8 @@ class Api::V1::WidgetsController < ApiController
61
71
  if widget.valid?
62
72
  head 201
63
73
  else
64
- render json: {
65
- errors: Stitches::Errors.from_active_record_object(widget)
74
+ render json: {
75
+ errors: Stitches::Errors.from_active_record_object(widget)
66
76
  }, status: 422
67
77
  end
68
78
  end
@@ -75,44 +85,44 @@ private
75
85
  end
76
86
  ```
77
87
 
78
- 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
79
89
  notable exceptions:
80
90
 
81
- * We aren't checking content type. A stitches-based microservice always uses JSON and refuses to route requests for non-JSON to
82
- you, so there's zero need to use `respond_to` and friends.
83
- * The error-building is structured and reliable.
84
- * This is an authenticated request. No request without proper authentication will be routed here, so you don't have to worry
85
- about it in your code.
86
- * This is a versioned request. While the URL will *not* contain `v1` in it, the `Accept` header will require a version and get
87
- 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.
88
98
 
89
- 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.
90
100
 
91
101
  ## More Info
92
102
 
93
103
  See [the wiki](https://github.com/stitchfix/stitches/wiki/Setup) for how to setup stitches.
94
104
 
95
- * [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:
96
106
  - Authorization via API key
97
107
  - Versioned requests via HTTP content types
98
108
  - Structured Errors
99
109
  - ISO 8601-formatted dates
100
110
  - Deprecation using the `Sunset` header
101
- * The [Generator](https://github.com/stitchfix/stitches/wiki/Generator) sets up some code in your app, so you can start writing
102
- 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:
103
114
  - a "ping" controller that can validate your app is working
104
115
  - version routing based on content-type (requests for V2 use the same URL, but are serviced by a different controller)
105
116
  - An ApiClient Active Record
106
117
  - Acceptance tests that can produce API documentation as they test your app.
107
- * Stitches provides [testing support](https://github.com/stitchfix/stitches/wiki/Testing)
108
-
118
+ - Stitches provides [testing support](https://github.com/stitchfix/stitches/wiki/Testing)
109
119
 
110
120
  ## Developing
111
121
 
112
- 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.
113
123
 
114
124
  Also, the integration test does a lot of "testing the implementation", but since Rails generators are notorious for silently
115
- 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
116
126
  any fancy refactors here, just keep it up to date.
117
127
 
118
128
  ---
@@ -1,4 +1,2 @@
1
1
  require 'stitches_norailtie'
2
2
  require 'stitches/railtie'
3
- # Add new stitches ruby files to the stitches_norailtie file instead of here
4
- require 'apitome'
@@ -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
@@ -4,7 +4,7 @@ module Stitches
4
4
  class ApiGenerator < Rails::Generators::Base
5
5
  include Rails::Generators::Migration
6
6
 
7
- source_root(File.expand_path(File.join(File.dirname(__FILE__),"generator_files")))
7
+ source_root(File.expand_path(File.join(File.dirname(__FILE__), "generator_files")))
8
8
 
9
9
  def self.next_migration_number(path)
10
10
  Time.now.utc.strftime("%Y%m%d%H%M%S")
@@ -12,14 +12,22 @@ 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
- inject_into_file "Gemfile", after: /^gem ['"]rails['"].*$/ do<<-GEM
15
+ gem "apitome"
16
+ gem_group :development, :test do
17
+ gem "rspec"
18
+ gem "rspec-rails"
19
+ gem "rspec_api_documentation"
20
+ end
16
21
 
17
- gem "apitome"
18
- gem "responders"
19
- gem "rspec_api_documentation", group: [ :development, :test ]
20
- gem "capybara", group: [ :development, :test ]
21
- GEM
22
+ Bundler.with_clean_env do
23
+ run "bundle install"
22
24
  end
25
+ generate "apitome:install"
26
+ generate "rspec:install"
27
+
28
+ gsub_file 'config/initializers/apitome.rb', /config.mount_at = .*$/, "config.mount_at = nil"
29
+ gsub_file 'config/initializers/apitome.rb', /config.title = .*$/, "config.title = 'Service Documentation'"
30
+
23
31
  inject_into_file "config/routes.rb", before: /^end/ do<<-ROUTES
24
32
  namespace :api do
25
33
  scope module: :v1, constraints: Stitches::ApiVersionConstraint.new(1) do
@@ -32,15 +40,14 @@ namespace :api do
32
40
  # as well as for your client to be able to validate this as well.
33
41
  end
34
42
  end
35
- api_docs = Rack::Auth::Basic.new(Apitome::Engine ) do |_,password|
43
+
44
+ api_docs = Rack::Auth::Basic.new(Apitome::Engine) do |_, password|
36
45
  password == ENV['HTTP_AUTH_PASSWORD']
37
46
  end
38
47
  mount api_docs, at: "docs"
39
48
  ROUTES
40
49
  end
41
50
 
42
- run 'bundle install'
43
-
44
51
  copy_file "app/controllers/api.rb"
45
52
  copy_file "app/controllers/api/api_controller.rb"
46
53
  copy_file "app/controllers/api/v1.rb"
@@ -53,8 +60,6 @@ mount api_docs, at: "docs"
53
60
  template "spec/features/api_spec.rb.erb", "spec/features/api_spec.rb"
54
61
  copy_file "spec/acceptance/ping_v1_spec.rb", "spec/acceptance/ping_v1_spec.rb"
55
62
 
56
- run 'bundle install'
57
-
58
63
  migration_template "db/migrate/enable_uuid_ossp_extension.rb", "db/migrate/enable_uuid_ossp_extension.rb"
59
64
  sleep 1 # allow clock to tick so we get different numbers
60
65
  migration_template "db/migrate/create_api_clients.rb", "db/migrate/create_api_clients.rb"
@@ -70,26 +75,23 @@ require 'stitches/spec'
70
75
 
71
76
  append_to_file 'spec/rails_helper.rb' do<<-RSPEC_API
72
77
  require 'rspec_api_documentation'
78
+
73
79
  RspecApiDocumentation.configure do |config|
74
- config.format = :json
75
- config.request_headers_to_include = %w(
76
- Accept
77
- Content-Type
78
- Authorization
79
- If-Modified-Since
80
- )
81
- config.response_headers_to_include = %w(
82
- Last-Modified
83
- ETag
84
- )
85
- config.api_name = "YOUR SERVICE NAME HERE"
80
+ config.format = :json
81
+ config.request_headers_to_include = %w(
82
+ Accept
83
+ Content-Type
84
+ Authorization
85
+ If-Modified-Since
86
+ )
87
+ config.response_headers_to_include = %w(
88
+ Last-Modified
89
+ ETag
90
+ )
91
+ config.api_name = "YOUR SERVICE NAME HERE"
86
92
  end
87
- RSPEC_API
93
+ RSPEC_API
88
94
  end
89
- run "rails g apitome:install"
90
- gsub_file 'config/initializers/apitome.rb', /config.mount_at = .*$/, "config.mount_at = nil"
91
- gsub_file 'config/initializers/apitome.rb', /config.title = .*$/, "config.title = 'Service Documentation'"
92
-
93
95
  end
94
96
  end
95
97
  end
@@ -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
- UnauthorizedResponse.new("key invalid",@realm,@configuration.custom_http_auth_scheme)
36
+ unauthorized_response("key invalid")
49
37
  end
50
38
  else
51
- UnauthorizedResponse.new("bad authorization type",@realm,@configuration.custom_http_auth_scheme)
39
+ unauthorized_response("bad authorization type")
52
40
  end
53
41
  else
54
- UnauthorizedResponse.new("no authorization header",@realm,@configuration.custom_http_auth_scheme)
42
+ unauthorized_response("no authorization header")
55
43
  end
56
44
  end
57
45
 
58
46
  private
59
47
 
60
- class UnauthorizedResponse < Rack::Response
61
- def initialize(reason,realm,custom_http_auth_scheme)
62
- super("Unauthorized - #{reason}", 401, { "WWW-Authenticate" => "#{custom_http_auth_scheme} realm=#{realm}" })
63
- end
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.
@@ -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
@@ -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=#{Rails.application.class.parent.to_s}', but was #{response['WWW-Authenticate']}"
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=#{Rails.application.class.parent.to_s}" ]
141
+ [response.response_code == 401, response.headers["WWW-Authenticate"] == "CustomKeyAuth realm=#{realm}"]
134
142
  end
135
143
  end
136
144
  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
@@ -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
- NotAcceptableResponse.new(accept)
19
+ not_acceptable_response(accept)
20
20
  end
21
21
  end
22
22
 
23
23
  private
24
24
 
25
- class NotAcceptableResponse < Rack::Response
26
- def initialize(accept_header)
27
- super("Not Acceptable - '#{accept_header}' didn't have the right mime type or version number. We only accept application/json with a version", 406)
28
- end
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Stitches
2
- VERSION = '3.7.3'
4
+ VERSION = '4.0.0.RC1'
3
5
  end
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "owners": [
3
3
  {
4
- "team": "platform"
4
+ "team": "devex"
5
5
  }
6
6
  ]
7
7
  }
@@ -0,0 +1,52 @@
1
+ require 'spec_helper.rb'
2
+
3
+ module MyApp
4
+ class Application
5
+ end
6
+ end
7
+
8
+ unless defined? ApiClient
9
+ class ApiClient
10
+ def self.column_names
11
+ ["enabled"]
12
+ end
13
+ end
14
+ end
15
+
16
+ describe Stitches::ApiClientAccessWrapper do
17
+ let(:api_client) {
18
+ double(ApiClient, id: 42)
19
+ }
20
+ before do
21
+ Stitches.configuration.reset_to_defaults!
22
+ end
23
+ describe '#fetch_by_key' do
24
+ context "cache is disabled" do
25
+ before do
26
+ expect(ApiClient).to receive(:find_by).and_return(api_client).twice
27
+ end
28
+
29
+ it "fetchs object from db twice" do
30
+ expect(described_class.fetch_for_key("123").id).to eq(42)
31
+ expect(described_class.fetch_for_key("123").id).to eq(42)
32
+ end
33
+ end
34
+
35
+ context "cache is configured" do
36
+ before do
37
+ Stitches.configure do |config|
38
+ config.max_cache_ttl = 5
39
+ config.max_cache_size = 10
40
+ end
41
+
42
+ expect(ApiClient).to receive(:find_by).and_return(api_client).once
43
+ end
44
+
45
+ it "fetchs object from cache" do
46
+ expect(described_class.fetch_for_key("123").id).to eq(42)
47
+ # This should hit the cache
48
+ expect(described_class.fetch_for_key("123").id).to eq(42)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -15,10 +15,8 @@ end
15
15
 
16
16
  describe Stitches::ApiKey do
17
17
  let(:app) { double("rack app") }
18
- let(:api_clients) {
19
- [
20
- double(ApiClient, id: 42)
21
- ]
18
+ let(:api_client) {
19
+ double(ApiClient, id: 42)
22
20
  }
23
21
 
24
22
  before do
@@ -27,23 +25,27 @@ describe Stitches::ApiKey do
27
25
  fake_rails_app = MyApp::Application.new
28
26
  allow(Rails).to receive(:application).and_return(fake_rails_app)
29
27
  allow(app).to receive(:call).with(env)
30
- allow(ApiClient).to receive(:where).and_return(api_clients)
28
+ allow(ApiClient).to receive(:find_by).and_return(api_client)
29
+ Stitches::ApiClientAccessWrapper.clear_api_cache
31
30
  end
32
31
 
33
32
  subject(:middleware) { described_class.new(app, namespace: "/api") }
34
33
 
35
34
  shared_examples "an unauthorized response" do
36
35
  it "returns a 401" do
37
- expect(@response.status).to eq(401)
36
+ status, _headers, _body = @response
37
+ expect(status).to eq(401)
38
38
  end
39
39
  it "sets the proper header" do
40
- expect(@response.headers["WWW-Authenticate"]).to eq("MyAwesomeInternalScheme realm=MyApp")
40
+ _status, headers, _body = @response
41
+ expect(headers["WWW-Authenticate"]).to eq("MyAwesomeInternalScheme realm=MyApp")
41
42
  end
42
43
  it "stops the call chain preventing anything from happening" do
43
44
  expect(app).not_to have_received(:call)
44
45
  end
45
46
  it "sends a reasonable message" do
46
- expect(@response.body).to eq([expected_body])
47
+ _status, _headers, body = @response
48
+ expect(body).to eq([expected_body])
47
49
  end
48
50
  end
49
51
 
@@ -155,18 +157,17 @@ describe Stitches::ApiKey do
155
157
  end
156
158
 
157
159
  it "sets the api_client's ID in the environment" do
158
- expect(env[Stitches.configuration.env_var_to_hold_api_client_primary_key]).to eq(api_clients.first.id)
160
+ expect(env[Stitches.configuration.env_var_to_hold_api_client_primary_key]).to eq(api_client.id)
159
161
  end
160
162
 
161
163
  it "sets the api_client itself in the environment" do
162
- expect(env[Stitches.configuration.env_var_to_hold_api_client]).to eq(api_clients.first)
164
+ expect(env[Stitches.configuration.env_var_to_hold_api_client]).to eq(api_client)
163
165
  end
164
166
  end
165
167
 
166
168
  context "unauthorized responses" do
167
169
  before do
168
170
  @response = middleware.call(env)
169
- @response.finish
170
171
  end
171
172
  context "invalid key" do
172
173
  let(:env) {
@@ -175,7 +176,7 @@ describe Stitches::ApiKey do
175
176
  "HTTP_AUTHORIZATION" => "MyAwesomeInternalScheme key=foobar",
176
177
  }
177
178
  }
178
- let(:api_clients) { [] }
179
+ let(:api_client) { nil }
179
180
 
180
181
  it_behaves_like "an unauthorized response" do
181
182
  let(:expected_body) { "Unauthorized - key invalid" }
@@ -9,17 +9,23 @@ describe Stitches::Configuration do
9
9
  let(:allowlist_regexp) { %r{foo} }
10
10
  let(:custom_http_auth_scheme) { "Blah" }
11
11
  let(:env_var_to_hold_api_client_primary_key) { "FOOBAR" }
12
+ let(:max_cache_ttl) { 11 }
13
+ let(:max_cache_size) { 111 }
12
14
 
13
15
  it "can be configured globally" do
14
16
  Stitches.configure do |config|
15
17
  config.allowlist_regexp = allowlist_regexp
16
18
  config.custom_http_auth_scheme = custom_http_auth_scheme
17
19
  config.env_var_to_hold_api_client_primary_key = env_var_to_hold_api_client_primary_key
20
+ config.max_cache_ttl = max_cache_ttl
21
+ config.max_cache_size = max_cache_size
18
22
  end
19
23
 
20
24
  expect(Stitches.configuration.allowlist_regexp).to eq(allowlist_regexp)
21
25
  expect(Stitches.configuration.custom_http_auth_scheme).to eq(custom_http_auth_scheme)
22
26
  expect(Stitches.configuration.env_var_to_hold_api_client_primary_key).to eq(env_var_to_hold_api_client_primary_key)
27
+ expect(Stitches.configuration.max_cache_ttl).to eq(max_cache_ttl)
28
+ expect(Stitches.configuration.max_cache_size).to eq(max_cache_size)
23
29
  end
24
30
 
25
31
  it "defaults to nil for allowlist_regexp" do
@@ -30,6 +36,14 @@ describe Stitches::Configuration do
30
36
  expect(Stitches.configuration.env_var_to_hold_api_client_primary_key).to eq("STITCHES_API_CLIENT_ID")
31
37
  end
32
38
 
39
+ it "defaults to 0 for max_cache_ttl" do
40
+ expect(Stitches.configuration.max_cache_ttl).to eq(0)
41
+ end
42
+
43
+ it "sets a default for max_cache_size" do
44
+ expect(Stitches.configuration.max_cache_size).to eq(0)
45
+ end
46
+
33
47
  it "blows up if you try to use custom_http_auth_scheme without having set it" do
34
48
  expect {
35
49
  Stitches.configuration.custom_http_auth_scheme
@@ -102,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") }
@@ -5,9 +5,10 @@ require "open3"
5
5
  RSpec.describe "Adding Stitches to a New Rails App", :integration do
6
6
  let(:work_dir) { Dir.mktmpdir }
7
7
  let(:rails_app_name) { "swamp-thing" }
8
+ let(:rails_root) { Pathname(work_dir) / rails_app_name }
8
9
 
9
10
  def run(command)
10
- stdout, stderr, stat = Open3.capture3(command)
11
+ stdout, stderr, stat = Open3.capture3({ 'BUNDLE_GEMFILE' => rails_root.join('Gemfile').to_path }, command)
11
12
  success = stat.success? && stdout !~ /Could not find generator/im
12
13
 
13
14
  if ENV["DEBUG"] == 'true' || !success
@@ -37,21 +38,30 @@ RSpec.describe "Adding Stitches to a New Rails App", :integration do
37
38
  "--no-rc",
38
39
  "--skip-bundle",
39
40
  ].join(" ")
40
- FileUtils.chdir work_dir do
41
- run rails_new
42
- FileUtils.chdir rails_app_name do
43
- example.run
41
+
42
+ # Use this local version of stitches rather than the one on Rubygems
43
+ gem_path = File.expand_path("../..", File.dirname(__FILE__))
44
+ use_local_stitches = %{echo "gem 'stitches', path: '#{gem_path}'" >> Gemfile}
45
+
46
+ Bundler.with_clean_env do
47
+ FileUtils.chdir work_dir do
48
+ run rails_new
49
+
50
+ FileUtils.chdir rails_app_name do
51
+ run use_local_stitches
52
+ # It's unclear why, but on CI the gems are not found when installed
53
+ # through bundler however installing them explicitly first fixes it.
54
+ run "gem install apitome rspec-rails rspec_api_documentation"
55
+ run "bundle install"
56
+ example.run
57
+ end
44
58
  end
45
59
  end
46
60
  end
47
61
 
48
62
  it "works as described in the README" do
49
- run "bin/rails generate rspec:install"
50
- run "bin/rails generate apitome:install"
51
63
  run "bin/rails generate stitches:api"
52
64
 
53
- rails_root = Pathname(work_dir) / rails_app_name
54
-
55
65
  # Yuck! So much duplication! BUT: Rails app templates have a notoriously silent failure mode, so mostly
56
66
  # what this is doing is ensuring that the generator inserted stuff when asked and that the very basics of what happens
57
67
  # during generation are there. It's gross, and I'm sorry.
@@ -60,9 +70,7 @@ RSpec.describe "Adding Stitches to a New Rails App", :integration do
60
70
  aggregate_failures do
61
71
  expect(File.exist?(rails_root / "app" / "controllers" / "api" / "api_controller.rb")).to eq(true)
62
72
  expect(rails_root / "Gemfile").to contain_gem("apitome")
63
- expect(rails_root / "Gemfile").to contain_gem("responders")
64
73
  expect(rails_root / "Gemfile").to contain_gem("rspec_api_documentation")
65
- expect(rails_root / "Gemfile").to contain_gem("capybara")
66
74
  expect(rails_root / "config" / "routes.rb").to have_route(namespace: :api, module_scope: :v1, resource: 'ping')
67
75
  expect(rails_root / "config" / "routes.rb").to have_route(namespace: :api, module_scope: :v2, resource: 'ping')
68
76
  expect(rails_root / "config" / "routes.rb").to have_mounted_engine("Apitome::Engine")
@@ -81,11 +89,7 @@ RSpec.describe "Adding Stitches to a New Rails App", :integration do
81
89
  end
82
90
 
83
91
  it "inserts the deprecation module into ApiController" do
84
- run "bin/rails generate rspec:install"
85
- run "bin/rails generate apitome:install"
86
92
  run "bin/rails generate stitches:api"
87
-
88
- rails_root = Pathname(work_dir) / rails_app_name
89
93
  api_controller = rails_root / "app" / "controllers" / "api" / "api_controller.rb"
90
94
 
91
95
  api_controller_contents = File.read(api_controller).split(/\n/)
@@ -106,11 +110,8 @@ RSpec.describe "Adding Stitches to a New Rails App", :integration do
106
110
  end
107
111
 
108
112
  it "inserts can update old configuration" do
109
- run "bin/rails generate rspec:install"
110
- run "bin/rails generate apitome:install"
111
113
  run "bin/rails generate stitches:api"
112
114
 
113
- rails_root = Pathname(work_dir) / rails_app_name
114
115
  initializer = rails_root / "config" / "initializers" / "stitches.rb"
115
116
 
116
117
  initializer_contents = File.read(initializer).split(/\n/)
@@ -11,13 +11,15 @@ describe Stitches::ValidMimeType do
11
11
 
12
12
  shared_examples "an unacceptable response" do
13
13
  it "returns a 406" do
14
- expect(@response.status).to eq(406)
14
+ status, _headers, _body = @response
15
+ expect(status).to eq(406)
15
16
  end
16
17
  it "stops the call chain preventing anything from happening" do
17
18
  expect(app).not_to have_received(:call)
18
19
  end
19
20
  it "sends a reasonable message" do
20
- expect(@response.body.first).to match(/didn't have the right mime type or version number. We only accept application\/json/)
21
+ _status, _headers, body = @response
22
+ expect(body.first).to match(/didn't have the right mime type or version number. We only accept application\/json/)
21
23
  end
22
24
  end
23
25
 
@@ -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) {
@@ -20,10 +20,9 @@ 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("rspec", ">= 3")
24
- s.add_runtime_dependency("rspec-rails", "~> 3")
25
- s.add_runtime_dependency("apitome")
23
+ s.add_runtime_dependency("lru_redux")
26
24
 
25
+ s.add_development_dependency("rspec", ">= 3")
27
26
  s.add_development_dependency("rake")
28
27
  s.add_development_dependency("rspec_junit_formatter")
29
28
  end
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.7.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: 2019-05-16 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
@@ -42,47 +42,33 @@ dependencies:
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0'
44
44
  - !ruby/object:Gem::Dependency
45
- name: rspec
45
+ name: lru_redux
46
46
  requirement: !ruby/object:Gem::Requirement
47
47
  requirements:
48
48
  - - ">="
49
49
  - !ruby/object:Gem::Version
50
- version: '3'
50
+ version: '0'
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
- version: '3'
58
- - !ruby/object:Gem::Dependency
59
- name: rspec-rails
60
- requirement: !ruby/object:Gem::Requirement
61
- requirements:
62
- - - "~>"
63
- - !ruby/object:Gem::Version
64
- version: '3'
65
- type: :runtime
66
- prerelease: false
67
- version_requirements: !ruby/object:Gem::Requirement
68
- requirements:
69
- - - "~>"
70
- - !ruby/object:Gem::Version
71
- version: '3'
57
+ version: '0'
72
58
  - !ruby/object:Gem::Dependency
73
- name: apitome
59
+ name: rspec
74
60
  requirement: !ruby/object:Gem::Requirement
75
61
  requirements:
76
62
  - - ">="
77
63
  - !ruby/object:Gem::Version
78
- version: '0'
79
- type: :runtime
64
+ version: '3'
65
+ type: :development
80
66
  prerelease: false
81
67
  version_requirements: !ruby/object:Gem::Requirement
82
68
  requirements:
83
69
  - - ">="
84
70
  - !ruby/object:Gem::Version
85
- version: '0'
71
+ version: '3'
86
72
  - !ruby/object:Gem::Dependency
87
73
  name: rake
88
74
  requirement: !ruby/object:Gem::Requirement
@@ -123,11 +109,12 @@ extensions: []
123
109
  extra_rdoc_files: []
124
110
  files:
125
111
  - ".circleci/config.yml"
112
+ - ".github/CODEOWNERS"
113
+ - ".github/PULL_REQUEST_TEMPLATE.md"
126
114
  - ".gitignore"
127
115
  - ".ruby-gemset"
128
116
  - ".ruby-version"
129
117
  - ".travis.yml"
130
- - CODEOWNERS
131
118
  - CODE_OF_CONDUCT.md
132
119
  - CONTRIBUTING.md
133
120
  - Gemfile
@@ -135,8 +122,8 @@ files:
135
122
  - Gemfile.rails-5.0
136
123
  - Gemfile.rails-5.1
137
124
  - Gemfile.rails-5.2
125
+ - Gemfile.rails-6.0
138
126
  - LICENSE.txt
139
- - PULL_REQUEST_TEMPLATE.md
140
127
  - README.md
141
128
  - Rakefile
142
129
  - build-matrix.json
@@ -144,6 +131,7 @@ files:
144
131
  - lib/stitches/add_deprecation_generator.rb
145
132
  - lib/stitches/add_enabled_to_api_clients_generator.rb
146
133
  - lib/stitches/allowlist_middleware.rb
134
+ - lib/stitches/api_client_access_wrapper.rb
147
135
  - lib/stitches/api_generator.rb
148
136
  - lib/stitches/api_key.rb
149
137
  - lib/stitches/api_version_constraint.rb
@@ -180,6 +168,7 @@ files:
180
168
  - lib/stitches/whitelisting_middleware.rb
181
169
  - lib/stitches_norailtie.rb
182
170
  - owners.json
171
+ - spec/api_client_access_wrapper_spec.rb
183
172
  - spec/api_key_spec.rb
184
173
  - spec/api_version_constraint_spec.rb
185
174
  - spec/configuration_spec.rb
@@ -208,16 +197,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
197
  version: '0'
209
198
  required_rubygems_version: !ruby/object:Gem::Requirement
210
199
  requirements:
211
- - - ">="
200
+ - - ">"
212
201
  - !ruby/object:Gem::Version
213
- version: '0'
202
+ version: 1.3.1
214
203
  requirements: []
215
- rubyforge_project:
216
- rubygems_version: 2.7.6
204
+ rubygems_version: 3.1.2
217
205
  signing_key:
218
206
  specification_version: 4
219
207
  summary: You'll be in stitches at how easy it is to create a service at Stitch Fix
220
208
  test_files:
209
+ - spec/api_client_access_wrapper_spec.rb
221
210
  - spec/api_key_spec.rb
222
211
  - spec/api_version_constraint_spec.rb
223
212
  - spec/configuration_spec.rb
@@ -1,21 +0,0 @@
1
- ## Problem
2
-
3
- «Brief overview of the problem»
4
-
5
- ## Solution
6
-
7
- «Brief description of how you solved the problem»
8
-
9
- ## Checklist
10
-
11
- ### Before Merging
12
-
13
- - [ ] If there is an RC on this branch, revert the version change in `version.rb`
14
-
15
- ### After Merging
16
-
17
- See the [gem release process](https://github.com/stitchfix/eng-wiki/blob/master/technical-topics/updating-gem-versions.md) for a detailed list, but the gist of it is:
18
-
19
- - [ ] Fetch `master` locally and run the applicable `rake version:*` task **on `master`** to bump the version
20
- - [ ] Run `rake release` **on `master`** to release the new version on Gemfury
21
- - [ ] Add [release notes](https://github.com/stitchfix/messaging/releases) - **this is very important in helping other engineers understand what changed in the new version**