we-call 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b5f8847896e17a2fec34ca12c0f3de5ac7a3462
4
+ data.tar.gz: 40f62ebf46c1096cdbb427e9b59314c5cc9b108b
5
+ SHA512:
6
+ metadata.gz: 5b3df1717cb053439ce90d57ce8087014959a47a65ac7b8ea42edf0dc15cbd672f60d0487076a0d3f5c2f30cbfe17711fc53d9e23946ac7a911f0886ec5edb9c
7
+ data.tar.gz: 28867ed3ab8fc0c9583ce732b288d81fcc50f9e1c802190da690f3ac9727b7074f18e337f3bb890c9cf82736da1e6183b8c45d3c76650e58703c3b42caf9a312
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.idea
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /gemfiles/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,25 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem install bundler
4
+ before_script:
5
+ - appraisal install
6
+ script:
7
+ - bundle exec appraisal rspec
8
+ gemfiles:
9
+ - faraday_0.9.gemfile
10
+ - faraday_0.10.gemfile
11
+ - faraday_0.11.gemfile
12
+ - faraday_0.12.gemfile
13
+ - faraday_0.13.gemfile
14
+ - rails_4.2.gemfile
15
+ - rails_5.0.gemfile
16
+ - rails_5.1.gemfile
17
+ rvm:
18
+ - 2.2.7
19
+ - 2.3.4
20
+ - 2.4.1
21
+ - ruby-head
22
+ matrix:
23
+ allow_failures:
24
+ - rvm: ruby-head
25
+ fast_finish: true
@@ -0,0 +1,31 @@
1
+ appraise "faraday-0.13" do
2
+ gem "faraday", "~> 0.13.0"
3
+ end
4
+
5
+ appraise "faraday-0.12" do
6
+ gem "faraday", "~> 0.12.0"
7
+ end
8
+
9
+ appraise "faraday-0.11" do
10
+ gem "faraday", "~> 0.11.0"
11
+ end
12
+
13
+ appraise "faraday-0.10" do
14
+ gem "faraday", "~> 0.10.0"
15
+ end
16
+
17
+ appraise "faraday-0.9" do
18
+ gem "faraday", "~> 0.9.0"
19
+ end
20
+
21
+ appraise "rails-5.1" do
22
+ gem "rails", "~> 5.1.0"
23
+ end
24
+
25
+ appraise "rails-5.0" do
26
+ gem "rails", "~> 5.0.0"
27
+ end
28
+
29
+ appraise "rails-4.2" do
30
+ gem "rails", "~> 4.2.0"
31
+ end
@@ -0,0 +1,38 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
+ and this project adheres to [Semantic Versioning](http://semver.org/).
7
+
8
+ ## Unreleased
9
+
10
+ ### Added
11
+ - Configurable `We::Call.configure` which accepts a config block
12
+ - Config option `config.app_name` to avoid providing `app:` in every connection initializer
13
+ - Added the concept of Annotations. Simply `extend We::Call::Annotations` in a base controller to get cool stuff
14
+ - First annotation: `We::Call::Deprecated` added to mark controller methods as deprecated
15
+ - Added `We::Call::Middleware::Client::DetectDeprecations` that automatically registers as a faraday response middleware to report deprecated endpoints
16
+
17
+ ### Changed
18
+ - Defaults to setting `X-App-Name` instead of `X-WeWork-App` (override with config.app_name_header)
19
+ - Defaults to setting `X-App-Env` instead of `X-WeWork-Env` (override with config.app_env_header)
20
+
21
+ ### Fixed
22
+ - Switched from manually requiring to using module autoload to reduce memory footprint
23
+
24
+ ## v0.4.2
25
+
26
+ ### Fixed
27
+ - Manually setting `conn.adapter` would result in double adapters (two requests made!)
28
+
29
+ ## v0.4.1
30
+
31
+ ### Fixed
32
+ - Improved support for Faraday 0.8 - 0.9.
33
+
34
+ ## v0.4.0
35
+
36
+ ### Added
37
+ - `We::Call::Connection.new` requires `timeout: 1` where 1 is seconds.
38
+ - `We::Call::Connection.new` accepts `open_timeout: 1` where 1 is seconds.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in we-call.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 WeWork Projects
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,189 @@
1
+ # We::Call
2
+
3
+ [![Build Status][travis-image]][travis-url]
4
+ [![Coverage Status][coveralls-image]][coveralls-url]
5
+ [![MIT License][license-image]][license-url]
6
+
7
+ ![Call me Maybe](https://cloud.githubusercontent.com/assets/67381/25590846/0c3145ea-2e80-11e7-9166-76448e0134a8.jpeg)
8
+
9
+ Requires metadata and offers client/server middleware for making HTTP requests, tracking calls, raising deprecations, supporting trace IDs, throttling, etc.
10
+
11
+ ## Goals
12
+
13
+ - Work just like Faraday out of the box
14
+ - Remove some of the guesswork that comes with HTTP service orientated architecures
15
+ - Provide sane defaults whenever possible, but ask for more information if required
16
+ - Facilitate [HTTP Evolution](https://www.mnot.net/blog/2012/12/04/api-evolution.html)
17
+
18
+ ## Usage
19
+
20
+
21
+ ```ruby
22
+ gem 'we-call'
23
+ ```
24
+
25
+ ```ruby
26
+ # config/initializers/we-call.rb
27
+
28
+ We::Call.configure do |config|
29
+ config.app_name = 'service-a' # default nil (Connection class falls back to APP_NAME or Rails name)
30
+ config.app_env = 'staging' # default nil (Connection class back to RACK_ENV || RAILS_ENV)
31
+ config.detect_deprecations = false # default true
32
+ end
33
+ ```
34
+
35
+ As this is a Faraday wrapper, the only thing that will change from normal Faraday usage is initialization.
36
+
37
+ ```ruby
38
+ connection = We::Call::Connection.new(host: 'https://some-service.example.com/', timeout: 5)
39
+
40
+ # or with a Faraday connection block
41
+ connection = We::Call::Connection.new(host: 'https://some-service.example.com/', timeout: 5) do |conn|
42
+ conn.token_auth('abc123token')
43
+ conn.headers['Foo'] = 'bar'
44
+ end
45
+ ```
46
+
47
+ See more connection block options in the [Faraday documentation](https://github.com/lostisland/faraday).
48
+
49
+ ### Provide an App
50
+
51
+ An application should provide its own name in the user agent when calling other services. This is important in case this app busts a local cache, causing it to stampeding herd other service(s).
52
+
53
+ Other services need to know which server is causing the problem, so no connections are allowed through `We::Call` without an app being set.
54
+
55
+ ```ruby
56
+ # Provided at config
57
+ connection = We::Call.configure do |config|
58
+ config.app_name = 'Service A'
59
+ end
60
+
61
+ # Provided at initialization
62
+ connection = We::Call::Connection.new(host: 'https://service-b.example.com/', app: 'Service A', timeout: 5)
63
+ ```
64
+
65
+ _Ofc services could lie about this, so do not use App Name for any sort of security. For that you need to use tokens assigned to applications. This is essentially just forcing a user agent._
66
+
67
+ ### Provide an Env
68
+
69
+ ```ruby
70
+ # Provided at config
71
+ connection = We::Call.configure do |config|
72
+ config.app_env = 'staging'
73
+ end
74
+
75
+ # Provided at initialization
76
+ connection = We::Call::Connection.new(host: 'https://service-b.example.com/', env: 'staging', timeout: 5)
77
+ ```
78
+
79
+ Not only is knowing the app name important, but knowing the env is necessary too. Sometimes people configure stuff wrong, and Service A (staging) will hit Service B (production) 😨.
80
+
81
+ If you are using Rack or Rails, you should not need to do this, as it'll use RACK_ENV or RAILS_ENV by default.
82
+
83
+ ### Timeouts
84
+
85
+ By default Faraday will let HTTP calls go on forever. In reality this is often 30 seconds for e.g: a Heroku app. Asking developers to make a choice about how long they're willing to wait on this call gives them a chance to consider an acceptable timeout.
86
+
87
+ The lower this number can be the better, as it reduces time web threads spend waiting for calls that are unlikely to respond anyway.
88
+
89
+ ```ruby
90
+ # Provided at initialization
91
+ connection = We::Call::Connection.new(host: 'https://service-b.example.com/', timeout: 5)
92
+ ```
93
+
94
+ Timeouts can only be provided at initialization of a connection, as they should be different for each service. This is down to the sad reality that some internal services are more performant than others, and various third-parties will have different SLAs.
95
+
96
+ As well as `timeout: num_seconds` which can set the entire open/read (essentially the total response time of the server), another optional argument exists for `open_timeout: numseconds`. This is how long We::Call should spend waiting for a vague sign of life from the server, which by default is 2.
97
+
98
+ ### Deprecations
99
+
100
+ We::Call helps with a bunch of things, such as the logic to go on client and server side to handle deprecations, both logging calls made to this servies deprecated endpoints, and alerting services when they make calls to deprecated endpoints.
101
+
102
+ Currently this is done using a simple annotation.
103
+
104
+ ```ruby
105
+ # app/controllers/api_controller.rb
106
+
107
+ extend We::Call::Annotations
108
+ ```
109
+
110
+ Then a controller can be annotated as such:
111
+
112
+ ```ruby
113
+ # app/controllers/foo_controller.rb
114
+
115
+ class FooController < ApiController
116
+ +We::Call::Deprecated.new(date: '2018-01-07 00:00:00 EDT')
117
+ def show
118
+ # ...
119
+ end
120
+ end
121
+ ```
122
+
123
+ It's as simple as that. This annotation will inject a [Sunset header](https://tools.ietf.org/html/draft-wilde-sunset-header-03) and everyone will know its being deprecated.
124
+
125
+ ## Middleware
126
+
127
+ ### Client
128
+
129
+ **DetectDeprecations**
130
+
131
+ Automatically enabled, this Faraday middleware will watch for the [Sunset header](https://tools.ietf.org/html/draft-wilde-sunset-header-03) and send warning to `ActiveSupport::Deprecation` if enabled, or to whatever is in `ENV['rake.logger']`.
132
+
133
+ ### Server
134
+
135
+ **LogUserAgent**
136
+
137
+ _(Optional)_ Log the User Agent, which might just be browser information (merely kinda handy), or could be an app name, like the one `We::Call::Connection` asks you for.
138
+
139
+ ```ruby
140
+ config.middleware.insert_after Rails::Rack::Logger, We::Call::Middleware::Server::LogUserAgent
141
+ ```
142
+
143
+ Easy! Check your logs for `user_agent=service-name; app_name=service-name;` The `app_name` will only show up if this was called by `We::Call::Connection` (as this is the only thing setting the `X-App-Name` header.)
144
+
145
+ ## Requirements
146
+
147
+ - **Ruby:** v2.2 - v2.4
148
+ - **Rails:** v4.2 - v5.1
149
+ - **Faraday:** v0.8 - v0.13
150
+
151
+ _For now this gem requires Rails 4.2+ due to some ActiveController functionality we are taking advantage of. Future work will include making this purely rack based._
152
+
153
+ ## TODO
154
+
155
+ - [ ] Support adding href to Deprecate to make a `Link` with rel=sunset as per Sunset RFC draft 03
156
+ - [ ] Remove Rails as a dependency (soft requirement on `ActiveSupport::Deprecated` is fine)
157
+ - [ ] Split DetectDeprecations into standalone `faraday-sunset` gem
158
+ - [ ] Pass Trace IDs along
159
+ - [ ] Work on sane defaults for retries and error raising
160
+
161
+ ## Testing
162
+
163
+ To run tests and modify locally, you'll want to `bundle install` in this directory.
164
+
165
+ ```
166
+ bundle exec rspec
167
+ ```
168
+
169
+ ## Development
170
+
171
+ If you want to test this gem within an application, update your Gemfile to have something like this: `gem 'we-call', github: 'wework/we-call', branch: 'BRANCHNAME'` and set your local config: `bundle config --local local.we-call path/to/we-call`
172
+
173
+ Simply revert the Gemfile change (updating the version as necessary!) and remove the config with `bundle config --delete local.we-call`.
174
+
175
+ References: [Blog Post](https://rossta.net/blog/how-to-specify-local-ruby-gems-in-your-gemfile.html) and [Bundle Documentation](https://bundler.io/v1.2/git.html#local)
176
+
177
+ ## Contributing
178
+
179
+ Bug reports and pull requests are welcome on GitHub at [wework/we-call](https://github.com/wework/we-call). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
180
+
181
+
182
+ [coveralls-image]:https://coveralls.io/repos/github/wework/we-call/badge.svg?branch=master
183
+ [coveralls-url]:https://coveralls.io/github/wework/we-call?branch=master
184
+
185
+ [travis-url]:https://travis-ci.org/wework/we-js-logger
186
+ [travis-image]: https://travis-ci.org/wework/we-js-logger.svg?branch=master
187
+
188
+ [license-url]: LICENSE
189
+ [license-image]: http://img.shields.io/badge/license-MIT-000000.svg?style=flat-square
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "we/call"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1 @@
1
+ require 'we/call'
@@ -0,0 +1,22 @@
1
+ module We
2
+ module Call
3
+ autoload :Annotations, "we/call/annotations"
4
+ autoload :Connection, "we/call/connection"
5
+ autoload :Configuration, "we/call/configuration"
6
+ autoload :Deprecated, "we/call/deprecated"
7
+ autoload :Middleware, "we/call/middleware"
8
+ autoload :VERSION, "we/call/version"
9
+
10
+ class << self
11
+ attr_accessor :configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def self.configure
19
+ yield(configuration)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ module We
2
+ module Call
3
+ module Annotations
4
+ # Enable annotations
5
+ include RubyDecorators
6
+
7
+ def self.extended(base)
8
+ base.class_eval do
9
+ # TODO Maybe this after_action could be a really simple call in base controllers
10
+ # Having it here obviously ties We::Call to Rails, and is probably more than the annotations
11
+ # class should be doing
12
+ after_action do |controller|
13
+
14
+ # TODO Maybe controller.class and params action could be passed to something in We::Call::Deprecated
15
+ klass = controller.class
16
+ method = params['action']
17
+ deprecation = We::Call::Deprecated.get("#{klass}##{method}")
18
+
19
+ if deprecation.present?
20
+ # Shove a deprecation warning into the console or wherever it goes
21
+ if defined? ActiveSupport
22
+ ActiveSupport::Deprecation.warn("#{klass}##{method} is deprecated for removal on #{deprecation[:date].iso8601}")
23
+ end
24
+
25
+ # Shove a Sunset header into HTTP Response for clients to sniff on
26
+ # https://tools.ietf.org/html/draft-wilde-sunset-header-03
27
+ response.headers['Sunset'] = deprecation[:date].httpdate
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ module We
5
+ module Call
6
+ class Configuration
7
+ attr_accessor :app_env, :app_env_header, :app_name, :app_name_header, :detect_deprecations
8
+
9
+ def initialize
10
+ @app_env_header = 'X-App-Env'
11
+ @app_name_header = 'X-App-Name'
12
+ @detect_deprecations = true
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,115 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ module We
5
+ module Call
6
+ module Connection
7
+ Faraday::Response.register_middleware detect_deprecations: We::Call::Middleware::Client::DetectDeprecations
8
+
9
+ extend self
10
+
11
+ OPEN_TIMEOUT = 2
12
+
13
+ class MissingApp < ArgumentError; end
14
+ class MissingEnv < ArgumentError; end
15
+ class MissingTimeout < ArgumentError; end
16
+ class MissingOpenTimeout < ArgumentError; end
17
+
18
+ parent_builder_class = defined?(Faraday::RackBuilder) ? Faraday::RackBuilder : Faraday::Builder
19
+
20
+ QueryableBuilder = Class.new(parent_builder_class) do
21
+ def adapter?
22
+ @has_adapter || false
23
+ end
24
+
25
+ def adapter(key, *args, &block)
26
+ super
27
+ @has_adapter = true
28
+ end
29
+ end
30
+
31
+ # @param [Object] host
32
+ # @param [Integer] timeout
33
+ # @param [Integer] open_timeout
34
+ # @param [String] app
35
+ # @param [String] env
36
+ # @yieldparam [Faraday::Connection] Faraday connection object is yielded to a block
37
+ def new(host:, timeout: nil, open_timeout: OPEN_TIMEOUT, app: guess_app, env: guess_env, &block)
38
+ @host = host
39
+ @app = app or raise_missing_app!
40
+ @env = env or raise_missing_env!
41
+ @timeout = timeout or raise_missing_timeout!
42
+ @open_timeout = open_timeout or raise_missing_open_timeout!
43
+ create(&block)
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :app, :env, :host, :timeout, :open_timeout
49
+
50
+ # @return [Faraday::Connection] Preconfigured Faraday Connection object, for hitting get, post, etc.
51
+ def create
52
+ builder = QueryableBuilder.new(&Proc.new { |_| })
53
+
54
+ ::Faraday.new(url: host, builder: builder) do |faraday|
55
+ faraday.headers['User-Agent'] = app
56
+ faraday.headers[config.app_name_header] = app
57
+ faraday.headers[config.app_env_header] = env
58
+ faraday.options[:timeout] = timeout
59
+ faraday.options[:open_timeout] = open_timeout
60
+
61
+ if config.detect_deprecations
62
+ if defined? ActiveSupport::Deprecation
63
+ faraday.response :detect_deprecations, active_support: true
64
+ else
65
+ faraday.response :detect_deprecations, logger: ENV['rack.logger']
66
+ end
67
+ end
68
+
69
+ yield faraday if block_given?
70
+
71
+ faraday.adapter Faraday.default_adapter unless faraday.builder.adapter?
72
+ end
73
+ end
74
+
75
+ def config
76
+ We::Call.configuration
77
+ end
78
+
79
+ def raise_missing_app!
80
+ raise MissingApp, 'app must be set, e.g: pokedex'
81
+ end
82
+
83
+ def raise_missing_env!
84
+ raise MissingEnv, 'env must be set, e.g: staging'
85
+ end
86
+
87
+ def raise_missing_timeout!
88
+ raise MissingTimeout, 'timeout must be set, maybe 5 (seconds) would be a good value. This is the open & read timeout, a.k.a max response time.'
89
+ end
90
+
91
+ def raise_missing_open_timeout!
92
+ raise MissingOpenTimeout, 'open_timeout must be set, and defaults to 1 second. This is the time until a connection is established with another server, and after 1 sec it\'s probably not there.'
93
+ end
94
+
95
+ # @return [String] Environment (usually 'development', 'staging', 'production', etc.)
96
+ def guess_env
97
+ return config.app_env if config.app_env
98
+ ENV['RAILS_ENV'] || ENV['RACK_ENV']
99
+ end
100
+
101
+ # @return [String] Check for config.app_name, or detect name from Rails application
102
+ def guess_app
103
+ return config.app_name if config.app_name
104
+ return ENV['APP_NAME'] if ENV['APP_NAME']
105
+ rails_app_name
106
+ end
107
+
108
+ def rails_app_name
109
+ if (defined? ::Rails) && !::Rails.application.nil?
110
+ ::Rails.application.class.parent_name.underscore.dasherize
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,40 @@
1
+ require "ruby_decorators"
2
+
3
+ module We
4
+ module Call
5
+ class Deprecated < RubyDecorator
6
+ @@methods = {}
7
+
8
+ def initialize(date:)
9
+ @date = date
10
+ end
11
+
12
+ def set(method, value)
13
+ @@methods[method] = value
14
+ end
15
+
16
+ def self.get(method)
17
+ @@methods[method]
18
+ end
19
+
20
+ def self.methods
21
+ @@methods
22
+ end
23
+
24
+ # Called when annotation is used
25
+ def call(this, *args, &blk)
26
+ set("#{this.owner}##{this.name}", date: normalize_datetime(@date))
27
+ this.call(*args, &blk)
28
+ end
29
+
30
+ protected
31
+
32
+ def normalize_datetime(datetime)
33
+ datetime = DateTime.parse(datetime) if datetime.is_a? String
34
+ datetime = datetime.to_datetime if datetime.respond_to? :to_datetime
35
+ return datetime if datetime.respond_to? :httpdate
36
+ raise TypeError, 'The date should be a Date, DateTime, Time or string containing a valid date and time'
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,8 @@
1
+ module We
2
+ module Call
3
+ module Middleware
4
+ autoload :Client, 'we/call/middleware/client'
5
+ autoload :Server, 'we/call/middleware/server'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module We
2
+ module Call
3
+ module Middleware
4
+ module Client
5
+ autoload :DetectDeprecations, "we/call/middleware/client/detect_deprecations"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,58 @@
1
+ # This class is going to be split into its own faraday-sunset soon, but I want to get the functionality nailed first
2
+ module We
3
+ module Call
4
+ module Middleware
5
+ module Client
6
+ class DetectDeprecations < Faraday::Middleware
7
+ class NoOutputForWarning < StandardError; end
8
+
9
+ # Initialize the middleware
10
+ #
11
+ # @param [Type] app describe app
12
+ # @param [Hash] options = {}
13
+ # @return void
14
+ def initialize(app, options = {})
15
+ super(app)
16
+ @options = options
17
+ end
18
+
19
+ # @param [Faraday::Env] no idea what this does
20
+ # @return [Faraday::Response] response from the middleware
21
+ def call(env)
22
+ @app.call(env).on_complete do |response_env|
23
+ datetime = sunset_header(response_env.response_headers)
24
+ report_deprecated_usage(env, datetime) unless datetime.nil?
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ # Check to see if there is a Sunset header, which contains deprecation date
31
+ #
32
+ # @param [Faraday::Response] response object with headers and whatnot
33
+ # @return [DateTime|nil] date time object of the expected deprecation date
34
+ def sunset_header(headers)
35
+ return if headers[:sunset].nil?
36
+ DateTime.parse(headers[:sunset])
37
+ end
38
+
39
+ def report_deprecated_usage(env, datetime)
40
+ if datetime > DateTime.now
41
+ warning = "Endpoint #{env.url} is deprecated for removal on #{datetime.iso8601}"
42
+ else
43
+ warning = "Endpoint #{env.url} was deprecated for removal on #{datetime.iso8601} and could be removed AT ANY TIME"
44
+ end
45
+
46
+ if @options[:active_support]
47
+ ActiveSupport::Deprecation.warn(warning)
48
+ elsif @options[:logger] && @options[:logger].respond_to?(:warn)
49
+ @options[:logger].warn(warning)
50
+ else
51
+ raise NoOutputForWarning, "Pass active_support: true or logger: SomeLoggerWithWriteMethod when registering middleware"
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ module We
2
+ module Call
3
+ module Middleware
4
+ module Server
5
+ autoload :LogUserAgent, 'we/call/middleware/server/log_user_agent'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module We
2
+ module Call
3
+ module Middleware
4
+ module Server
5
+ class LogUserAgent
6
+ def initialize app
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ line = "user_agent=#{env['HTTP_USER_AGENT']};"
12
+ line += " app_name=#{env[incoming_app_name_header]};" if env[incoming_app_name_header]
13
+ line += " app_env=#{env[incoming_app_env_header]};" if env[incoming_app_env_header]
14
+ output(line)
15
+ @app.call(env)
16
+ end
17
+
18
+ private
19
+
20
+ def output(line)
21
+ puts line
22
+ end
23
+
24
+ def config
25
+ We::Call.configuration
26
+ end
27
+
28
+ def incoming_app_env_header
29
+ @incoming_app_env_header ||= "HTTP_#{config.app_env_header.upcase.gsub!(/-/, '_')}"
30
+ end
31
+
32
+ def incoming_app_name_header
33
+ @incoming_app_name_header ||= "HTTP_#{config.app_name_header.upcase.gsub!(/-/, '_')}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ module We
2
+ module Call
3
+ VERSION = "0.5.0"
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'we/call/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "we-call"
8
+ spec.version = We::Call::VERSION
9
+ spec.authors = ["WeWork Engineering"]
10
+ spec.email = ["engineering@wework.com"]
11
+
12
+ spec.summary = "Making healthy happy HTTP calls"
13
+ spec.description = "Handles conventions of making calls to other services, with required metadata for tracking calls between services, deprecations of endpoints, trace IDs, throttling, etc."
14
+ spec.homepage = "https://github.com/wework/we-call-gem"
15
+ spec.licenses = ['MIT']
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(spec)/})
19
+ end
20
+ spec.bindir = "bin"
21
+ spec.require_paths = ["lib"]
22
+ spec.metadata["yard.run"] = "yri"
23
+
24
+ spec.add_dependency "faraday", ">= 0.9.0", "< 0.14"
25
+ spec.add_dependency "faraday_middleware", '~> 0'
26
+ spec.add_dependency "ruby_decorators", '~> 0.0'
27
+ spec.add_dependency "rails", ">= 4.2"
28
+
29
+ spec.add_development_dependency "appraisal", "~> 2"
30
+ spec.add_development_dependency "coveralls", '~> 0.7'
31
+ spec.add_development_dependency "bundler", "~> 1.14"
32
+ spec.add_development_dependency "rake", "~> 12.0"
33
+ spec.add_development_dependency "rspec", "~> 3.5"
34
+ spec.add_development_dependency "simplecov", '~> 0.15'
35
+ spec.add_development_dependency "hashie", "~> 3.5"
36
+ spec.add_development_dependency "pry"
37
+ spec.add_development_dependency "yard"
38
+ spec.add_development_dependency "vcr", '~> 3'
39
+ end
metadata ADDED
@@ -0,0 +1,274 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: we-call
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - WeWork Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.14'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.14'
33
+ - !ruby/object:Gem::Dependency
34
+ name: faraday_middleware
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: ruby_decorators
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rails
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '4.2'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '4.2'
75
+ - !ruby/object:Gem::Dependency
76
+ name: appraisal
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2'
89
+ - !ruby/object:Gem::Dependency
90
+ name: coveralls
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.7'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.7'
103
+ - !ruby/object:Gem::Dependency
104
+ name: bundler
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.14'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.14'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rake
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '12.0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '12.0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: rspec
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '3.5'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '3.5'
145
+ - !ruby/object:Gem::Dependency
146
+ name: simplecov
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.15'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '0.15'
159
+ - !ruby/object:Gem::Dependency
160
+ name: hashie
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '3.5'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '3.5'
173
+ - !ruby/object:Gem::Dependency
174
+ name: pry
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ - !ruby/object:Gem::Dependency
188
+ name: yard
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ - !ruby/object:Gem::Dependency
202
+ name: vcr
203
+ requirement: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '3'
208
+ type: :development
209
+ prerelease: false
210
+ version_requirements: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '3'
215
+ description: Handles conventions of making calls to other services, with required
216
+ metadata for tracking calls between services, deprecations of endpoints, trace IDs,
217
+ throttling, etc.
218
+ email:
219
+ - engineering@wework.com
220
+ executables: []
221
+ extensions: []
222
+ extra_rdoc_files: []
223
+ files:
224
+ - ".coveralls.yml"
225
+ - ".gitignore"
226
+ - ".rspec"
227
+ - ".travis.yml"
228
+ - Appraisals
229
+ - CHANGELOG
230
+ - Gemfile
231
+ - LICENSE
232
+ - README.md
233
+ - Rakefile
234
+ - bin/console
235
+ - bin/setup
236
+ - lib/we-call.rb
237
+ - lib/we/call.rb
238
+ - lib/we/call/annotations.rb
239
+ - lib/we/call/configuration.rb
240
+ - lib/we/call/connection.rb
241
+ - lib/we/call/deprecated.rb
242
+ - lib/we/call/middleware.rb
243
+ - lib/we/call/middleware/client.rb
244
+ - lib/we/call/middleware/client/detect_deprecations.rb
245
+ - lib/we/call/middleware/server.rb
246
+ - lib/we/call/middleware/server/log_user_agent.rb
247
+ - lib/we/call/version.rb
248
+ - we-call.gemspec
249
+ homepage: https://github.com/wework/we-call-gem
250
+ licenses:
251
+ - MIT
252
+ metadata:
253
+ yard.run: yri
254
+ post_install_message:
255
+ rdoc_options: []
256
+ require_paths:
257
+ - lib
258
+ required_ruby_version: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - ">="
261
+ - !ruby/object:Gem::Version
262
+ version: '0'
263
+ required_rubygems_version: !ruby/object:Gem::Requirement
264
+ requirements:
265
+ - - ">="
266
+ - !ruby/object:Gem::Version
267
+ version: '0'
268
+ requirements: []
269
+ rubyforge_project:
270
+ rubygems_version: 2.6.8
271
+ signing_key:
272
+ specification_version: 4
273
+ summary: Making healthy happy HTTP calls
274
+ test_files: []