stitches 5.0.0 → 5.1.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: 162beab8fe18efd29717c69f7d1ff734ef6822f1355addc8699b4e1079eef741
4
- data.tar.gz: 9e0d31dc4943c09889b0ddee7226963ed2cf82b13709c0f9f5a0c06603bd1990
3
+ metadata.gz: 7e87005e3080477f7dc3d927596ea5cdeab14f4b004cd20744894088866f5b2b
4
+ data.tar.gz: e50f73f746ccc1b283bad98619f9a4e70a56f91c3f0e11faec66e375cfb7055f
5
5
  SHA512:
6
- metadata.gz: 15a4b24194f3b19a930b99894d8be0ba7cac737830b50e71e763e3e66df7e944c9615692d0f3b854faeaf422cc8fa82fdb4e73ac025ed7b0ce9ccf4f673af8f0
7
- data.tar.gz: 62a6dc4a6c5430f9556747ff931cb92d94a55b0d1c943c4b1dbbe53d96e2338cefc57ed7ea0513c52d69f60a8ab1455224be8bfbb5c2dba172c4ef83d3ab60fb
6
+ metadata.gz: 84cf005517476feb055dbb1d22142d3b17749a7cdd01cb1135de3c00c9d29ca40e034dd2ee23a5d259bcf346458e1aea12cef301145df583f587fb9038d38b63
7
+ data.tar.gz: 4e592659a77f77ff2f6eb7c6d3b217ab6f36fd52dcf2fdaf3f16ad0e2950f7a8af69efa9a7c880d13100307aecf513c6eed4a1f31392ea1067a97ff9a21634b1
data/.circleci/config.yml CHANGED
@@ -15,21 +15,21 @@ parameters:
15
15
  default: ""
16
16
  old_ruby:
17
17
  type: string
18
- default: "3.2.4"
18
+ default: "3.3.9"
19
19
  current_ruby:
20
20
  type: string
21
- default: "3.3.2"
21
+ default: "3.4.5"
22
22
  old_rails:
23
23
  type: string
24
- default: "7.0.8.4"
24
+ default: "8.0.3"
25
25
  current_rails:
26
26
  type: string
27
- default: "7.1.3.4"
27
+ default: "8.1.2"
28
28
 
29
29
  jobs:
30
30
  generate-and-push-docs:
31
31
  docker:
32
- - image: cimg/ruby:3.3.2
32
+ - image: cimg/ruby:3.3.7
33
33
  auth:
34
34
  username: "$DOCKERHUB_USERNAME"
35
35
  password: "$DOCKERHUB_PASSWORD"
@@ -52,7 +52,7 @@ jobs:
52
52
  docs:push; fi
53
53
  release:
54
54
  docker:
55
- - image: cimg/ruby:3.3.2
55
+ - image: cimg/ruby:3.3.7
56
56
  auth:
57
57
  username: "$DOCKERHUB_USERNAME"
58
58
  password: "$DOCKERHUB_PASSWORD"
@@ -126,7 +126,7 @@ jobs:
126
126
  fi
127
127
  - run:
128
128
  name: Notify Pager Duty
129
- command: bundle exec y-notify "#app-platform-ops"
129
+ command: bundle exec y-notify "#pire-ops"
130
130
  when: on_fail
131
131
  - store_test_results:
132
132
  path: "/tmp/test-results"
data/.github/CODEOWNERS CHANGED
@@ -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
- * @stitchfix/app-platform
11
+ * @stitchfix/platform-insights-and-reliability-engineering
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.2.3
1
+ ruby-3.4.9
data/CODE_OF_CONDUCT.md CHANGED
@@ -1,6 +1,136 @@
1
1
  # Code of Conduct
2
2
 
3
- We are committed to keeping our community open and inclusive.
3
+ This code of conduct outlines our expectations for participants within
4
+ the **Stitch Fix** community, as well as steps to
5
+ reporting unacceptable behavior. We are committed to providing a
6
+ welcoming and inspiring community for all and expect our code of conduct
7
+ to be honored. Anyone who violates this code of conduct may be banned
8
+ from the community.
4
9
 
5
- **Our Code of Conduct can be found here**:
6
- http://opensource.stitchfix.com/code-of-conduct.html
10
+ Our open source community strives to:
11
+
12
+ - **Be friendly and patient.**
13
+ - **Be welcoming**: We strive to be a community
14
+ that welcomes and supports people of all backgrounds and identities.
15
+ This includes, but is not limited to members of any race, ethnicity,
16
+ culture, national origin, colour, immigration status, social and
17
+ economic class, educational level, sex, sexual orientation, gender
18
+ identity and expression, age, size, family status, political belief,
19
+ religion, and mental and physical ability.
20
+ - **Be considerate**: Your work will be used by
21
+ other people, and you in turn will depend on the work of others. Any
22
+ decision you take will affect users and colleagues, and you should
23
+ take those consequences into account when making decisions. Remember
24
+ that we’re a world-wide community, so you might not be communicating
25
+ in someone else’s primary language.
26
+ - **Be respectful**: Not all of us will agree all
27
+ the time, but disagreement is no excuse for poor behavior and poor
28
+ manners. We might all experience some frustration now and then, but we
29
+ cannot allow that frustration to turn into a personal attack. It’s
30
+ important to remember that a community where people feel uncomfortable
31
+ or threatened is not a productive one.
32
+ - **Be careful in the words that we choose**: we
33
+ are a community of professionals, and we conduct ourselves
34
+ professionally. Be kind to others. Do not insult or put down other
35
+ participants. Harassment and other exclusionary behavior aren’t
36
+ acceptable.
37
+ - **Try to understand why we disagree**:
38
+ Disagreements, both social and technical, happen all the time. It is
39
+ important that we resolve disagreements and differing views
40
+ constructively. Remember that we’re different. The strength of our
41
+ community comes from its diversity, people from a wide range of
42
+ backgrounds. Different people have different perspectives on issues.
43
+ Being unable to understand why someone holds a viewpoint doesn’t mean
44
+ that they’re wrong. Don’t forget that it is human to err and blaming
45
+ each other doesn’t get us anywhere. Instead, focus on helping to
46
+ resolve issues and learning from mistakes.
47
+
48
+ ## Definitions
49
+
50
+ Harassment includes, but is not limited to:
51
+
52
+ - Offensive comments related to gender, gender identity and expression,
53
+ sexual orientation, disability, mental illness, neuro(a)typicality,
54
+ physical appearance, body size, race, age, regional discrimination,
55
+ political or religious affiliation
56
+ - Unwelcome comments regarding a person’s lifestyle choices and
57
+ practices, including those related to food, health, parenting, drugs,
58
+ and employment
59
+ - Deliberate misgendering. This includes deadnaming or persistently
60
+ using a pronoun that does not correctly reflect a person’s gender
61
+ identity. You must address people by the name they give you when not
62
+ addressing them by their username or handle
63
+ - Physical contact and simulated physical contact (eg, textual
64
+ descriptions like “*hug*” or “*backrub*”) without consent or after a
65
+ request to stop
66
+ - Threats of violence, both physical and psychological
67
+ - Incitement of violence towards any individual, including encouraging a
68
+ person to commit suicide or to engage in self-harm
69
+ - Deliberate intimidation
70
+ - Stalking or following
71
+ - Harassing photography or recording, including logging online activity
72
+ for harassment purposes
73
+ - Sustained disruption of discussion
74
+ - Unwelcome sexual attention, including gratuitous or off-topic sexual
75
+ images or behaviour
76
+ - Pattern of inappropriate social contact, such as requesting/assuming
77
+ inappropriate levels of intimacy with others
78
+ - Continued one-on-one communication after requests to cease
79
+ - Deliberate “outing” of any aspect of a person’s identity without their
80
+ consent except as necessary to protect others from intentional abuse
81
+ - Publication of non-harassing private communication
82
+
83
+ ### Diversity Statement
84
+
85
+ We encourage everyone to participate and are committed to building a
86
+ community for all. Although we will fail at times, we seek to treat
87
+ everyone both as fairly and equally as possible. Whenever a participant
88
+ has made a mistake, we expect them to take responsibility for it. If
89
+ someone has been harmed or offended, it is our responsibility to listen
90
+ carefully and respectfully, and do our best to right the wrong.
91
+
92
+ Although this list cannot be exhaustive, we explicitly honor diversity
93
+ in age, gender, gender identity or expression, culture, ethnicity,
94
+ language, national origin, political beliefs, profession, race,
95
+ religion, sexual orientation, socioeconomic status, and technical
96
+ ability. We will not tolerate discrimination based on any of the
97
+ protected characteristics above, including participants with
98
+ disabilities.
99
+
100
+ ### Reporting Issues
101
+
102
+ If you experience or witness unacceptable behavior—or have any other
103
+ concerns—please report it by contacting us via
104
+ **opensource@stitchfix.com**. All reports will be
105
+ handled with discretion. In your report please include:
106
+
107
+ - Your contact information.
108
+ - Names (real, nicknames, or pseudonyms) of any individuals involved. If
109
+ there are additional witnesses, please include them as well. Your
110
+ account of what occurred, and if you believe the incident is ongoing.
111
+ If there is a publicly available record (e.g. a mailing list archive
112
+ or a public IRC logger), please include a link.
113
+ - Any additional information that may be helpful.
114
+
115
+ After filing a report, a representative will contact you personally,
116
+ review the incident, follow up with any additional questions, and make a
117
+ decision as to how to respond. If the person who is harassing you is
118
+ part of the response team, they will recuse themselves from handling
119
+ your incident. If the complaint originates from a member of the response
120
+ team, it will be handled by a different member of the response team. We
121
+ will respect confidentiality requests for the purpose of protecting
122
+ victims of abuse.
123
+
124
+ ### Attribution & Acknowledgements
125
+
126
+ We all stand on the shoulders of giants across many open source
127
+ communities. We’d like to thank the communities and projects that
128
+ established code of conducts and diversity statements as our
129
+ inspiration:
130
+
131
+ - [Django](https://www.djangoproject.com/conduct/reporting/)
132
+ - [Python](https://www.python.org/community/diversity/)
133
+ - [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct)
134
+ - [Contributor Covenant](http://contributor-covenant.org/)
135
+ - [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/)
136
+ - [Citizen Code of Conduct](http://citizencodeofconduct.org/)
data/README.md CHANGED
@@ -42,6 +42,78 @@ Stitches.configure do |config|
42
42
  end
43
43
  ```
44
44
 
45
+ ### Caller Identification via `X-StitchFix-Calling-Service`
46
+
47
+ When API key auth is disabled, services lose the ability to identify which
48
+ internal service is calling them. The `Stitches::CallingServiceName` concern
49
+ provides a replacement that works with or without API keys enabled.
50
+
51
+ **Include the concern in your API controller:**
52
+
53
+ ```ruby
54
+ class Api::ApiController < ActionController::API
55
+ include Stitches::CallingServiceName
56
+
57
+ # Replace api_client.name with calling_service_name anywhere you need
58
+ # to know who the caller is (stats, routing, updated_by, etc.)
59
+ end
60
+ ```
61
+
62
+ For controllers that don't inherit from your ApiController (e.g. Devise
63
+ controllers), include the concern directly:
64
+
65
+ ```ruby
66
+ class Api::V1::SessionsController < Devise::SessionsController
67
+ include Stitches::CallingServiceName
68
+ end
69
+ ```
70
+
71
+ **Resolution order:**
72
+
73
+ 1. `X-StitchFix-Calling-Service` request header (preferred)
74
+ 2. `api_client&.name` — the stitches-authenticated ApiClient (works when API keys are enabled)
75
+ 3. `"N/A"` — fallback when neither is available
76
+
77
+ This is backwards compatible. When `disable_api_key_support` is false, the
78
+ stitches middleware still populates `api_client`, so the fallback returns the
79
+ authenticated name. When true, callers identify themselves via the header.
80
+
81
+ **Client-side setup:**
82
+
83
+ Clients using `stitchfix-api_client` (v5.1+) automatically send this header.
84
+ The value is derived from the `app_name` config option or `ENV["APP_NAME"]`:
85
+
86
+ ```ruby
87
+ MyServiceClient.new(
88
+ endpoint: "https://my-service.internal",
89
+ app_name: "kingmob" # sent as X-StitchFix-Calling-Service
90
+ )
91
+ ```
92
+
93
+ If `app_name` is not set, `ENV["APP_NAME"]` is used (typically set in
94
+ production deployments). If neither is available, the header is not sent.
95
+
96
+ #### Security considerations
97
+
98
+ `X-StitchFix-Calling-Service` is a **self-declared, unsigned header**. Any
99
+ caller that can reach your service can set it to any value. This means:
100
+
101
+ - **Do not use `calling_service_name` as a sole authorization mechanism for
102
+ sensitive operations** (e.g. granting admin tokens, bypassing verification
103
+ flows) unless the network layer guarantees that only the legitimate service
104
+ can reach the endpoint.
105
+ - **Strip this header at public ingress points** (Traefik, ALB, API gateway)
106
+ to prevent external callers from spoofing internal service identities.
107
+ Internal-only traffic should be the only source of this header.
108
+ - **This header replaces API key-based identity, not authorization.** API
109
+ keys provided a weak form of authentication (possession of a shared secret).
110
+ This header provides identification only. If you need to verify the caller's
111
+ identity cryptographically, use mTLS or a service mesh AuthorizationPolicy.
112
+ - **Safe uses:** stats tagging, logging, `updated_by` audit fields, routing
113
+ hints, non-security-critical behavioral branching.
114
+ - **Unsafe without network enforcement:** access control decisions, privilege
115
+ escalation gates, bypassing user-facing safety flows.
116
+
45
117
  ### Upgrading from an older version
46
118
 
47
119
  - When upgrading to version 4.0.0 and above you may now take advantage of an in-memory cache
@@ -180,6 +252,12 @@ Also, the integration test does a lot of "testing the implementation", but since
180
252
  failing with a successful result, we have to make sure that the various `inject_into_file` calls are actually working. Do not do
181
253
  any fancy refactors here, just keep it up to date.
182
254
 
255
+ ## Ruby / Rails version support
256
+
257
+ This gem attempts to support the most recent 2 major/minor versions of Ruby and Rails. This is a moving
258
+ target, and we make a best effort to track to this policy. Older versions _may_ work, but supporting
259
+ those versions is outside of the scope of what we intend to maintain.
260
+
183
261
  ## Releases
184
262
 
185
263
  See the release process for open source gems in the Stitch Fix engineering wiki under technical topics.
data/build-matrix.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "build_matrix": {
3
+ "artisinally-hand-crafted": true
4
+ }
5
+ }
@@ -41,7 +41,13 @@ module Stitches
41
41
  unauthorized_response("bad authorization type")
42
42
  end
43
43
  else
44
- unauthorized_response("no authorization header")
44
+ message = "no authorization header"
45
+
46
+ if Rails.env.test? || Rails.env.development?
47
+ message += " (Development/Test Env Hint: Blocked by stitches; confirm your authorization header is set OR check the `allowlist_regex` config for this path)"
48
+ end
49
+
50
+ unauthorized_response(message)
45
51
  end
46
52
  end
47
53
 
@@ -0,0 +1,12 @@
1
+ module Stitches
2
+ module CallingServiceName
3
+ HEADER = "X-StitchFix-Calling-Service"
4
+
5
+ def calling_service_name
6
+ @calling_service_name ||=
7
+ request.headers[HEADER].presence ||
8
+ api_client&.name ||
9
+ "N/A"
10
+ end
11
+ end
12
+ end
@@ -1,4 +1,5 @@
1
1
  class Api::ApiController < ActionController::API
2
+ include Stitches::CallingServiceName
2
3
  include Stitches::Deprecation
3
4
  #
4
5
  # The order of the rescue_from blocks is important - ActiveRecord::RecordNotFound must come after StandardError,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stitches
4
- VERSION = '5.0.0'
4
+ VERSION = '5.1.0.RC1'
5
5
  end
@@ -18,5 +18,6 @@ require 'stitches/add_enabled_to_api_clients_generator'
18
18
  require 'stitches/add_disabled_at_to_api_clients_generator'
19
19
  require 'stitches/api_version_constraint'
20
20
  require 'stitches/api_key'
21
+ require 'stitches/calling_service_name'
21
22
  require 'stitches/deprecation'
22
23
  require 'stitches/valid_mime_type'
@@ -40,9 +40,9 @@ RSpec.describe "/api/hellos", type: :request do
40
40
  let(:version) { 6 }
41
41
 
42
42
  it "fails to map to a controller" do
43
- expect {
44
- get "/api/hellos", headers: headers
45
- }.to raise_error(ActionController::RoutingError)
43
+ get "/api/hellos", headers: headers
44
+
45
+ expect(response).to be_not_found
46
46
  end
47
47
  end
48
48
 
@@ -50,9 +50,9 @@ RSpec.describe "/api/hellos", type: :request do
50
50
  let(:accept_header) { "application/json" }
51
51
 
52
52
  it "fails to map to a controller" do
53
- expect {
54
- get "/api/hellos", headers: headers
55
- }.to raise_error(ActionController::RoutingError)
53
+ get "/api/hellos", headers: headers
54
+
55
+ expect(response).to be_not_found
56
56
  end
57
57
  end
58
58
  end
@@ -0,0 +1,71 @@
1
+ require 'rails_helper'
2
+
3
+ describe Stitches::CallingServiceName do
4
+ let(:headers) { {} }
5
+ let(:fake_request) { double("request", headers: headers) }
6
+ let(:fake_api_client) { nil }
7
+ let(:fake_controller) {
8
+ req = fake_request
9
+ client = fake_api_client
10
+ Object.new.tap { |c|
11
+ c.extend(described_class)
12
+ c.define_singleton_method(:request) { req }
13
+ c.define_singleton_method(:api_client) { client }
14
+ }
15
+ }
16
+
17
+ describe "#calling_service_name" do
18
+ context "when X-StitchFix-Calling-Service header is present" do
19
+ let(:headers) { {"X-StitchFix-Calling-Service" => "kingmob"} }
20
+
21
+ it "returns the header value" do
22
+ expect(fake_controller.calling_service_name).to eq("kingmob")
23
+ end
24
+
25
+ context "and api_client is also present" do
26
+ let(:fake_api_client) { double("ApiClient", name: "other-service") }
27
+
28
+ it "prefers the header" do
29
+ expect(fake_controller.calling_service_name).to eq("kingmob")
30
+ end
31
+ end
32
+ end
33
+
34
+ context "when header is absent but api_client is present" do
35
+ let(:fake_api_client) { double("ApiClient", name: "mobile-service") }
36
+
37
+ it "returns the api_client name" do
38
+ expect(fake_controller.calling_service_name).to eq("mobile-service")
39
+ end
40
+ end
41
+
42
+ context "when header is blank" do
43
+ let(:headers) { {"X-StitchFix-Calling-Service" => ""} }
44
+ let(:fake_api_client) { double("ApiClient", name: "fallback-service") }
45
+
46
+ it "treats blank as absent and falls through to api_client" do
47
+ expect(fake_controller.calling_service_name).to eq("fallback-service")
48
+ end
49
+ end
50
+
51
+ context "when neither header nor api_client is present" do
52
+ it "returns 'unknown'" do
53
+ expect(fake_controller.calling_service_name).to eq("N/A")
54
+ end
55
+ end
56
+
57
+ context "when api_client is nil" do
58
+ let(:fake_api_client) { nil }
59
+
60
+ it "returns 'unknown'" do
61
+ expect(fake_controller.calling_service_name).to eq("N/A")
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "::HEADER" do
67
+ it "is the expected header name" do
68
+ expect(described_class::HEADER).to eq("X-StitchFix-Calling-Service")
69
+ end
70
+ end
71
+ end
@@ -1 +1 @@
1
- ruby-3.2.3
1
+ ruby-3.3.7
@@ -1,10 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
- ruby '3.2.3'
4
+ ruby '3.3.7'
5
5
 
6
6
  # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
7
- gem 'rails', '~> 7.1.3'
7
+ gem 'rails', '~> 7.2.2'
8
8
  # Use postgres as the database for Active Record
9
9
  gem 'pg'
10
10
  # Use Puma as the app server
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stitches
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0.RC1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stitch Fix Engineering
8
8
  - Andrew Peterson
9
9
  - Dave Copeland
10
10
  - Jonathan Dean
11
- autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2024-06-20 00:00:00.000000000 Z
13
+ date: 1980-01-02 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: rails
@@ -158,13 +157,13 @@ files:
158
157
  - ".gitignore"
159
158
  - ".ruby-gemset"
160
159
  - ".ruby-version"
161
- - ".travis.yml"
162
160
  - CODE_OF_CONDUCT.md
163
161
  - CONTRIBUTING.md
164
162
  - Gemfile
165
163
  - LICENSE.txt
166
164
  - README.md
167
165
  - Rakefile
166
+ - build-matrix.json
168
167
  - lib/stitches.rb
169
168
  - lib/stitches/add_deprecation_generator.rb
170
169
  - lib/stitches/add_disabled_at_to_api_clients_generator.rb
@@ -175,6 +174,7 @@ files:
175
174
  - lib/stitches/api_key.rb
176
175
  - lib/stitches/api_migration_generator.rb
177
176
  - lib/stitches/api_version_constraint.rb
177
+ - lib/stitches/calling_service_name.rb
178
178
  - lib/stitches/configuration.rb
179
179
  - lib/stitches/deprecation.rb
180
180
  - lib/stitches/error.rb
@@ -209,6 +209,7 @@ files:
209
209
  - owners.json
210
210
  - spec/api_key_middleware_spec.rb
211
211
  - spec/api_version_constraint_middleware_spec.rb
212
+ - spec/calling_service_name_spec.rb
212
213
  - spec/configuration_spec.rb
213
214
  - spec/deprecation_spec.rb
214
215
  - spec/error_spec.rb
@@ -285,7 +286,6 @@ homepage: https://github.com/stitchfix/stitches
285
286
  licenses:
286
287
  - MIT
287
288
  metadata: {}
288
- post_install_message:
289
289
  rdoc_options: []
290
290
  require_paths:
291
291
  - lib
@@ -300,13 +300,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
300
300
  - !ruby/object:Gem::Version
301
301
  version: '0'
302
302
  requirements: []
303
- rubygems_version: 3.4.19
304
- signing_key:
303
+ rubygems_version: 3.6.9
305
304
  specification_version: 4
306
305
  summary: You'll be in stitches at how easy it is to create a service at Stitch Fix
307
306
  test_files:
308
307
  - spec/api_key_middleware_spec.rb
309
308
  - spec/api_version_constraint_middleware_spec.rb
309
+ - spec/calling_service_name_spec.rb
310
310
  - spec/configuration_spec.rb
311
311
  - spec/deprecation_spec.rb
312
312
  - spec/error_spec.rb
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.6
4
- - 2.7
5
- - ruby-head
6
- notifications:
7
- email: false
8
- jobs:
9
- allow_failures:
10
- - rvm: ruby-head