diff --git a/lib/grape/middleware/globals.rb b/lib/grape/middleware/globals.rb index ec3aa1695..40f892e88 100644 --- a/lib/grape/middleware/globals.rb +++ b/lib/grape/middleware/globals.rb @@ -1,12 +1,21 @@ +# This Middleware is not loaded by grape by default. +# If you intend to use it you must first: +# require 'grape/middleware/globals' +# See the spec for examples. +# require 'grape/middleware/base' module Grape module Middleware class Globals < Base + # NOTE: If you have Grape mounted as a Rack endpoint in a Rails stack action dispatch may have moved the params + # If your params are not showing up, + # then you may need to override this method and + # have it load params from @env['action_dispatch.request.request_parameters'] instead of request.params def before - @env['grape.request'] = Grape::Request.new(@env) + request = Grape::Request.new(@env) @env['grape.request.headers'] = request.headers - @env['grape.request.params'] = request.params if @env['rack.input'] + @env['grape.request.params'] = request.params end end end diff --git a/lib/grape/middleware/logger.rb b/lib/grape/middleware/logger.rb new file mode 100644 index 000000000..ca30b6808 --- /dev/null +++ b/lib/grape/middleware/logger.rb @@ -0,0 +1,73 @@ +# This Middleware is not loaded by grape by default. +# If you intend to use it you must first: +# require 'grape/middleware/logger' +# See the spec for examples. +# +require 'grape/middleware/globals' + +module Grape + module Middleware + class Logger < Globals + def before + super + logger.info "[api] Requested#{request_log}" unless request_log.nil? || request_log.empty? + logger.info "[api] HEADERS: #{@env['grape.request.headers']}" unless @env['grape.request.headers'].nil? || @env['grape.request.headers'].empty? + logger.info "[api] PARAMS: #{@env['grape.request.params']}" unless @env['grape.request.params'].nil? || @env['grape.request.params'].empty? + end + + # Example of what you might do in a subclass in an after hook: + # def after + # response_body = JSON.parse(response.body.first) + # if response_body.is_a?(Hash) + # logger.debug "[api] RespType: #{response_body['response_type']}" unless response_body['response_type'].blank? + # logger.debug "[api] Response: #{response_body['response']}" unless response_body['response'].blank? + # logger.debug "[api] Backtrace:\n#{response_body['backtrace'].join("\n")}" if response_body['backtrace'] && response_body['backtrace'].any? + # end + # super + # end + + private + + # Override in a subclass to customize the logger (not affected by setting the logger helper in Grape::API) + # def logger + # @logger ||= Rails.logger # as an example + # end + def logger + @logger ||= Logger.new(STDOUT) + end + + def request_log + @request_log ||= begin + res = '' + res << " #{request_log_data}" unless request_log_data.nil? || request_log_data.empty? + res + end + end + + def request_log_data + rld = {} + + x_org = env['HTTP_X_ORGANIZATION'] + + rld[:user_id] = current_user.id if current_user + rld[:x_organization] = x_org if x_org + + rld + end + + # Override this method in a subclass and mount the subclass + # For example with Devise & Warden: + # def current_user + # @warden_user_for_log ||= begin + # woo = env['warden'].instance_variable_get(:'@users') + # woo[:user] if woo + # end + # end + # Also if Warden is later in your Rack Middleware stack + # then you can only render the current user data in the after hook, not the before hook. + def current_user + false + end + end + end +end diff --git a/spec/grape/middleware/globals_spec.rb b/spec/grape/middleware/globals_spec.rb new file mode 100644 index 000000000..2d1794955 --- /dev/null +++ b/spec/grape/middleware/globals_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +require 'grape/middleware/globals' + +describe Grape::Middleware::Globals do + let(:app) { lambda { |env| [200, env, 'Howdy Doody'] } } + subject { Grape::Middleware::Globals.new(app, {}) } + + context 'with params' do + it 'sets the params based on the params' do + env = Rack::MockRequest.env_for('/awesome', params: { 'swank' => 'bank' }) + expect(subject.call(env)[1]['grape.request.params']).to eq('swank' => 'bank') + expect(subject.call(env)[1]['grape.request.headers']).to eq({}) + end + end + + context 'with headers' do + it 'sets the headers based on the headers' do + env = Rack::MockRequest.env_for('/awesome', params: {}) + env['HTTP_MONKEY'] = 'I_BANANA' + + expect(subject.call(env)[1]['grape.request.params']).to eq({}) + expect(subject.call(env)[1]['grape.request.headers']).to eq('Monkey' => 'I_BANANA') + end + end + + context 'with headers and params' do + it 'sets the headers based on the headers' do + env = Rack::MockRequest.env_for('/awesome', params: { 'grapes' => 'wrath' }) + env['HTTP_MONKEY'] = 'I_BANANA' + + expect(subject.call(env)[1]['grape.request.params']).to eq('grapes' => 'wrath') + expect(subject.call(env)[1]['grape.request.headers']).to eq('Monkey' => 'I_BANANA') + end + end +end