stitches 5.1.0.RC2 → 5.1.0.RC4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +6 -28
- data/lib/stitches/calling_service_client.rb +15 -0
- data/lib/stitches/calling_service_middleware.rb +29 -0
- data/lib/stitches/calling_service_name.rb +9 -4
- data/lib/stitches/configuration.rb +11 -0
- data/lib/stitches/railtie.rb +2 -0
- data/lib/stitches/version.rb +1 -1
- data/spec/calling_service_middleware_spec.rb +72 -0
- data/spec/calling_service_name_spec.rb +16 -55
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e1a4aefc1d0249b7b5c8caf2eb06aa6e0866afef4f1d4564d63efa239ef80ce
|
|
4
|
+
data.tar.gz: 322ab581e74df9bb933d8a1e23d2dc3973c6e3494ebfac23424f16c25cbf589a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5da26d78de12b20c29f2cff453dae8b1b1712ab3f0bbcd373291082eeee1feae3f22dcfb841c512f6a351243f10894b1c91dbc6c5c58400c182be9c0e96ee93c
|
|
7
|
+
data.tar.gz: 1a2ef89f2e74dfd9478158dfa747c19989de5f32425363f77ae428058468e57d934e29decae0fb1b1bc39a60b1b18abb97c8c82bfd3dfde73ac0b449dc13c995
|
data/.circleci/config.yml
CHANGED
|
@@ -26,6 +26,9 @@ parameters:
|
|
|
26
26
|
type: string
|
|
27
27
|
default: "8.1.2"
|
|
28
28
|
|
|
29
|
+
orbs:
|
|
30
|
+
stitchfix_build: stitchfix/build@dev:master
|
|
31
|
+
|
|
29
32
|
jobs:
|
|
30
33
|
generate-and-push-docs:
|
|
31
34
|
docker:
|
|
@@ -45,29 +48,12 @@ jobs:
|
|
|
45
48
|
docs:generate:custom ; elif [[ $(bundle exec rake -T docs:generate) ]];
|
|
46
49
|
then echo "Generating docs using rake task docs:generate" ; bundle exec
|
|
47
50
|
rake docs:generate ; else echo "Skipping doc generation" ; exit 0 ; fi '
|
|
51
|
+
- stitchfix_build/aws_cli_setup_eng
|
|
48
52
|
- run:
|
|
49
53
|
name: Push documentation to Unwritten
|
|
50
54
|
command:
|
|
51
55
|
if [[ $(bundle exec rake -T docs:push) ]]; then bundle exec rake
|
|
52
56
|
docs:push; fi
|
|
53
|
-
release:
|
|
54
|
-
docker:
|
|
55
|
-
- image: cimg/ruby:3.3.7
|
|
56
|
-
auth:
|
|
57
|
-
username: "$DOCKERHUB_USERNAME"
|
|
58
|
-
password: "$DOCKERHUB_PASSWORD"
|
|
59
|
-
steps:
|
|
60
|
-
- checkout
|
|
61
|
-
- run: bundle config stitchfix01.jfrog.io $ARTIFACTORY_USER:$ARTIFACTORY_TOKEN
|
|
62
|
-
- run: bundle install
|
|
63
|
-
- run:
|
|
64
|
-
name: Artifactory login
|
|
65
|
-
command:
|
|
66
|
-
mkdir -p ~/.gem && curl -u$ARTIFACTORY_USER:$ARTIFACTORY_TOKEN https://stitchfix01.jfrog.io/stitchfix01/api/gems/eng-gems/api/v1/api_key.yaml
|
|
67
|
-
> ~/.gem/credentials && chmod 0600 ~/.gem/credentials
|
|
68
|
-
- run:
|
|
69
|
-
name: Build/release gem to artifactory
|
|
70
|
-
command: bundle exec rake push_artifactory
|
|
71
57
|
test:
|
|
72
58
|
parameters:
|
|
73
59
|
ruby-version:
|
|
@@ -87,6 +73,7 @@ jobs:
|
|
|
87
73
|
auth:
|
|
88
74
|
username: "$DOCKERHUB_USERNAME"
|
|
89
75
|
password: "$DOCKERHUB_PASSWORD"
|
|
76
|
+
resource_class: medium.gen2
|
|
90
77
|
working_directory: "~/stitches"
|
|
91
78
|
environment:
|
|
92
79
|
DATABASE_URL: "postgres://postgres:@localhost:5432/stitches_fake_app_test"
|
|
@@ -136,19 +123,10 @@ workflows:
|
|
|
136
123
|
unless:
|
|
137
124
|
equal: ["schedule", << pipeline.parameters.GHA_Event >>]
|
|
138
125
|
jobs:
|
|
139
|
-
- release:
|
|
140
|
-
context: org-global
|
|
141
|
-
requires:
|
|
142
|
-
- test
|
|
143
|
-
filters:
|
|
144
|
-
tags:
|
|
145
|
-
only: "/^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:(-|\\.)(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$/"
|
|
146
|
-
branches:
|
|
147
|
-
ignore: /.*/
|
|
148
126
|
- generate-and-push-docs:
|
|
149
127
|
context: org-global
|
|
150
128
|
requires:
|
|
151
|
-
-
|
|
129
|
+
- test
|
|
152
130
|
filters:
|
|
153
131
|
tags:
|
|
154
132
|
only: "/^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:(-|\\.)(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$/"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Stitches
|
|
2
|
+
# Lightweight stand-in for ApiClient when the caller is identified by
|
|
3
|
+
# the X-StitchFix-Calling-Service header rather than an API key lookup.
|
|
4
|
+
# Implements the same interface (.name, .id, .key) so existing code
|
|
5
|
+
# that reads api_client.name continues to work.
|
|
6
|
+
CallingServiceClient = Struct.new(:name) do
|
|
7
|
+
def id
|
|
8
|
+
nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def key
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative 'calling_service_client'
|
|
2
|
+
|
|
3
|
+
module Stitches
|
|
4
|
+
# Rack middleware that populates the api_client env var from the
|
|
5
|
+
# X-StitchFix-Calling-Service header when no other auth middleware
|
|
6
|
+
# has already set it. This allows existing code that reads
|
|
7
|
+
# api_client.name to work transparently after API keys are disabled.
|
|
8
|
+
class CallingServiceMiddleware
|
|
9
|
+
def initialize(app)
|
|
10
|
+
@app = app
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
client_key = Stitches.configuration.env_var_to_hold_api_client
|
|
15
|
+
|
|
16
|
+
unless env[client_key]
|
|
17
|
+
header_name = Stitches.configuration.calling_service_header
|
|
18
|
+
header_name = CallingServiceName::DEFAULT_CALLING_SERVICE_HEADER unless header_name.present?
|
|
19
|
+
rack_key = "HTTP_#{header_name.upcase.tr('-', '_')}"
|
|
20
|
+
|
|
21
|
+
if (name = env[rack_key]).present?
|
|
22
|
+
env[client_key] = CallingServiceClient.new(name)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@app.call(env)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
module Stitches
|
|
2
2
|
module CallingServiceName
|
|
3
|
-
|
|
3
|
+
DEFAULT_CALLING_SERVICE_HEADER = "X-StitchFix-Calling-Service"
|
|
4
4
|
|
|
5
5
|
def calling_service_name
|
|
6
6
|
@calling_service_name ||=
|
|
7
|
-
request.headers[
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
request.headers[calling_service_header_name].presence || ""
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def calling_service_header_name
|
|
13
|
+
configured = Stitches.configuration.calling_service_header
|
|
14
|
+
configured.present? ? configured : DEFAULT_CALLING_SERVICE_HEADER
|
|
10
15
|
end
|
|
11
16
|
end
|
|
12
17
|
end
|
|
@@ -13,6 +13,7 @@ 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
|
+
@calling_service_header = NonNullString.new("calling_service_header", "X-StitchFix-Calling-Service")
|
|
16
17
|
@max_cache_ttl = NonNullInteger.new("max_cache_ttl", 0)
|
|
17
18
|
@max_cache_size = NonNullInteger.new("max_cache_size", 0)
|
|
18
19
|
@disabled_key_leniency_in_seconds = ActiveSupport::Duration.days(3)
|
|
@@ -61,6 +62,16 @@ class Stitches::Configuration
|
|
|
61
62
|
@env_var_to_hold_api_client= NonNullString.new("env_var_to_hold_api_client",new_env_var_to_hold_api_client)
|
|
62
63
|
end
|
|
63
64
|
|
|
65
|
+
# The name of the HTTP header used to identify the calling service.
|
|
66
|
+
# Clients send this header; servers read it via CallingServiceName.
|
|
67
|
+
def calling_service_header
|
|
68
|
+
@calling_service_header.to_s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def calling_service_header=(new_calling_service_header)
|
|
72
|
+
@calling_service_header = NonNullString.new("calling_service_header", new_calling_service_header)
|
|
73
|
+
end
|
|
74
|
+
|
|
64
75
|
def max_cache_ttl
|
|
65
76
|
@max_cache_ttl.to_i
|
|
66
77
|
end
|
data/lib/stitches/railtie.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require 'stitches/api_key'
|
|
2
2
|
require 'stitches/valid_mime_type'
|
|
3
3
|
require 'stitches/api_client_access_wrapper'
|
|
4
|
+
require 'stitches/calling_service_middleware'
|
|
4
5
|
|
|
5
6
|
module Stitches
|
|
6
7
|
class Railtie < Rails::Railtie
|
|
7
8
|
config.app_middleware.use Stitches::ApiKey
|
|
9
|
+
config.app_middleware.use Stitches::CallingServiceMiddleware
|
|
8
10
|
config.app_middleware.use Stitches::ValidMimeType
|
|
9
11
|
end
|
|
10
12
|
end
|
data/lib/stitches/version.rb
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'rails_helper'
|
|
2
|
+
|
|
3
|
+
describe Stitches::CallingServiceMiddleware do
|
|
4
|
+
let(:app) { ->(env) { [200, env, "OK"] } }
|
|
5
|
+
let(:middleware) { described_class.new(app) }
|
|
6
|
+
let(:env) { Rack::MockRequest.env_for("/api/test", method: "POST") }
|
|
7
|
+
|
|
8
|
+
let(:client_key) { Stitches.configuration.env_var_to_hold_api_client }
|
|
9
|
+
|
|
10
|
+
describe "#call" do
|
|
11
|
+
context "when the header is present and env var is not set" do
|
|
12
|
+
before { env["HTTP_X_STITCHFIX_CALLING_SERVICE"] = "kingmob" }
|
|
13
|
+
|
|
14
|
+
it "populates the env var with a CallingServiceClient" do
|
|
15
|
+
_status, result_env, _body = middleware.call(env)
|
|
16
|
+
client = result_env[client_key]
|
|
17
|
+
|
|
18
|
+
expect(client).to be_a(Stitches::CallingServiceClient)
|
|
19
|
+
expect(client.name).to eq("kingmob")
|
|
20
|
+
expect(client.id).to be_nil
|
|
21
|
+
expect(client.key).to be_nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "when the env var is already set (API key or JWT auth ran)" do
|
|
26
|
+
let(:existing_client) { double("ApiClient", name: "existing-client", id: 42) }
|
|
27
|
+
|
|
28
|
+
before do
|
|
29
|
+
env[client_key] = existing_client
|
|
30
|
+
env["HTTP_X_STITCHFIX_CALLING_SERVICE"] = "some-service"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "does not overwrite the existing value" do
|
|
34
|
+
_status, result_env, _body = middleware.call(env)
|
|
35
|
+
expect(result_env[client_key]).to eq(existing_client)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "when the header is absent and env var is not set" do
|
|
40
|
+
it "leaves the env var nil" do
|
|
41
|
+
_status, result_env, _body = middleware.call(env)
|
|
42
|
+
expect(result_env[client_key]).to be_nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "when the header is blank" do
|
|
47
|
+
before { env["HTTP_X_STITCHFIX_CALLING_SERVICE"] = "" }
|
|
48
|
+
|
|
49
|
+
it "leaves the env var nil" do
|
|
50
|
+
_status, result_env, _body = middleware.call(env)
|
|
51
|
+
expect(result_env[client_key]).to be_nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context "with a custom configured header" do
|
|
56
|
+
before do
|
|
57
|
+
Stitches.configuration.calling_service_header = "X-Custom-Caller"
|
|
58
|
+
env["HTTP_X_CUSTOM_CALLER"] = "custom-service"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
after { Stitches.configuration.reset_to_defaults! }
|
|
62
|
+
|
|
63
|
+
it "reads from the configured header" do
|
|
64
|
+
_status, result_env, _body = middleware.call(env)
|
|
65
|
+
client = result_env[client_key]
|
|
66
|
+
|
|
67
|
+
expect(client).to be_a(Stitches::CallingServiceClient)
|
|
68
|
+
expect(client.name).to eq("custom-service")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -3,91 +3,52 @@ require 'rails_helper'
|
|
|
3
3
|
describe Stitches::CallingServiceName do
|
|
4
4
|
let(:headers) { {} }
|
|
5
5
|
let(:fake_request) { double("request", headers: headers) }
|
|
6
|
-
let(:fake_api_client) { nil }
|
|
7
6
|
let(:fake_controller) {
|
|
8
7
|
req = fake_request
|
|
9
|
-
client = fake_api_client
|
|
10
8
|
Object.new.tap { |c|
|
|
11
9
|
c.extend(described_class)
|
|
12
10
|
c.define_singleton_method(:request) { req }
|
|
13
|
-
c.define_singleton_method(:api_client) { client }
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
describe "#calling_service_name" do
|
|
18
|
-
context "when
|
|
15
|
+
context "when the header is present" do
|
|
19
16
|
let(:headers) { {"X-StitchFix-Calling-Service" => "kingmob"} }
|
|
20
17
|
|
|
21
18
|
it "returns the header value" do
|
|
22
19
|
expect(fake_controller.calling_service_name).to eq("kingmob")
|
|
23
20
|
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
21
|
end
|
|
50
22
|
|
|
51
|
-
context "when
|
|
52
|
-
it "returns
|
|
23
|
+
context "when the header is absent" do
|
|
24
|
+
it "returns empty string" do
|
|
53
25
|
expect(fake_controller.calling_service_name).to eq("")
|
|
54
26
|
end
|
|
55
27
|
end
|
|
56
28
|
|
|
57
|
-
context "when
|
|
58
|
-
let(:
|
|
29
|
+
context "when the header is blank" do
|
|
30
|
+
let(:headers) { {"X-StitchFix-Calling-Service" => ""} }
|
|
59
31
|
|
|
60
32
|
it "returns empty string" do
|
|
61
33
|
expect(fake_controller.calling_service_name).to eq("")
|
|
62
34
|
end
|
|
63
35
|
end
|
|
36
|
+
end
|
|
64
37
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
c.extend(described_class)
|
|
70
|
-
c.define_singleton_method(:request) { req }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
38
|
+
describe "configurable header" do
|
|
39
|
+
it "defaults to X-StitchFix-Calling-Service" do
|
|
40
|
+
expect(Stitches.configuration.calling_service_header).to eq("X-StitchFix-Calling-Service")
|
|
41
|
+
end
|
|
73
42
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
end
|
|
43
|
+
context "when configured to a custom header" do
|
|
44
|
+
let(:headers) { {"X-Custom-Caller" => "my-service"} }
|
|
77
45
|
|
|
78
|
-
|
|
79
|
-
|
|
46
|
+
before { Stitches.configuration.calling_service_header = "X-Custom-Caller" }
|
|
47
|
+
after { Stitches.configuration.reset_to_defaults! }
|
|
80
48
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
end
|
|
49
|
+
it "reads from the configured header" do
|
|
50
|
+
expect(fake_controller.calling_service_name).to eq("my-service")
|
|
84
51
|
end
|
|
85
52
|
end
|
|
86
53
|
end
|
|
87
|
-
|
|
88
|
-
describe "::HEADER" do
|
|
89
|
-
it "is the expected header name" do
|
|
90
|
-
expect(described_class::HEADER).to eq("X-StitchFix-Calling-Service")
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
54
|
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: 5.1.0.
|
|
4
|
+
version: 5.1.0.RC4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stitch Fix Engineering
|
|
@@ -174,6 +174,8 @@ files:
|
|
|
174
174
|
- lib/stitches/api_key.rb
|
|
175
175
|
- lib/stitches/api_migration_generator.rb
|
|
176
176
|
- lib/stitches/api_version_constraint.rb
|
|
177
|
+
- lib/stitches/calling_service_client.rb
|
|
178
|
+
- lib/stitches/calling_service_middleware.rb
|
|
177
179
|
- lib/stitches/calling_service_name.rb
|
|
178
180
|
- lib/stitches/configuration.rb
|
|
179
181
|
- lib/stitches/deprecation.rb
|
|
@@ -209,6 +211,7 @@ files:
|
|
|
209
211
|
- owners.json
|
|
210
212
|
- spec/api_key_middleware_spec.rb
|
|
211
213
|
- spec/api_version_constraint_middleware_spec.rb
|
|
214
|
+
- spec/calling_service_middleware_spec.rb
|
|
212
215
|
- spec/calling_service_name_spec.rb
|
|
213
216
|
- spec/configuration_spec.rb
|
|
214
217
|
- spec/deprecation_spec.rb
|
|
@@ -306,6 +309,7 @@ summary: You'll be in stitches at how easy it is to create a service at Stitch F
|
|
|
306
309
|
test_files:
|
|
307
310
|
- spec/api_key_middleware_spec.rb
|
|
308
311
|
- spec/api_version_constraint_middleware_spec.rb
|
|
312
|
+
- spec/calling_service_middleware_spec.rb
|
|
309
313
|
- spec/calling_service_name_spec.rb
|
|
310
314
|
- spec/configuration_spec.rb
|
|
311
315
|
- spec/deprecation_spec.rb
|