Consumer App showing error - "no implicit conversion of Array into Hash" when API returns error json in a custom format
Rails: 4.2.0
Ruby: 2.2.1
Her: 0.7.3
Devise: 3.4.1
Simple Token Authentication: 1.8.0
I have a Rails 4 application, ApiApp which exposes its API via relative endpoints /api/<resource_name_here>. Sample code snippets from this app (which exposes API)
module Api
class ClientsController < ApiController
def index
clients = current_user.provider.clients
render json: clients
end
...
...
end
end
module Api
class ApiController < ActionController::Base
acts_as_token_authentication_handler_for User, fallback_to_devise: false
before_filter :check_authentication!
private
# Overridden version of Simple Token Authentication gem's authenticate_entity_from_token!(entity) method to handle token authentication in customized way
#https://github.com/gonzalo-bulnes/simple_token_authentication/blob/1985632a36cef6a6b8182e3c45a75a2d39f7ee51/lib/simple_token_authentication/token_authentication_handler.rb#L29
def authenticate_entity_from_token!(entity)
...
...
...
perform_sign_in!(user, sign_in_handler) # when user is found and token is matched and verified to be valid
end
def check_authentication!
# This variable is set in authenticate_entity_from_token!(entity) above to retain any
# relevant error information to pass back to the API's client.
if @authenticate_entity_from_token_error_message
render_unauthorized_error_json(@authenticate_entity_from_token_error_message)
return
end
render json: { status: :unauthorized } unless current_user
end
def render_unauthorized_error_json(error_message)
render json: {
status: :unauthorized,
error: error_message
}
end
Now I am creating a new Rails 4, DataApp which needs to access ApiApp's data via latter's API. Code snippets from DataApp application:
Her Initializer
# https://github.com/remiprev/her#authentication
# Reference:
# http://stackoverflow.com/questions/23402820/rails-server-bin-rails6-warning-already-initialized- constant-app-path-error#comment46052336_23424073
require "#{Rails.root}/lib/middleware/my_api_authentication_token"
# Settings is made available by rails_config gem
my_api_url = Settings.api.url
Her::API.setup url: my_api_url do |config|
# Request
config.use MyApiAuthenticationToken
config.use Faraday::Request::UrlEncoded
config.use Her::Middleware::AcceptJSON
# Response
config.use Her::Middleware::DefaultParseJSON
# Adapter
config.use Faraday::Adapter::NetHttp
end
Her Model: data_app/app/models/api_app/client.rb
module ApiApp
class Client
include Her::Model
end
end
data_app/app/controllers/api_app/clients_controller.rb
class ApiApp::ClientsController < ApplicationController
before_action :authenticate_user!
def index
begin
@clients = ApiApp::Client.all
@clients.first.inspect
rescue Exception => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
end
end
end
To access ApiApp's data, DataApp needs to send an access token to it (which is expirable) . This token I am sending, using a Faraday::Middleware as documented here in a request header X-User-Token expected by Simple Token Authentication gem.
Api::ClientsController in ApiApp is successfully receiving the access token sent by DataApp request to it. However the token is found to be expired by the token verification logic in my ApiApp's Api::ApiController#authenticate_entity_from_token!(entity). Thus ApiApp is returning following json:
render json: {
status: :unauthorized,
error: "API Access Token is found expired. Please obtain a new access token."
}
Server logs from ApiApp after DataApp sent request to access ApiApp's Client resource (which is ActiveRecord based)
Started GET "/api/clients" for 127.0.0.1 at 2015-03-06 19:40:51 +0530
Processing by Api::ClientsController#index as JSON
Parameters: {"subdomain"=>"api"}
Filter chain halted as :check_authentication! rendered or redirected
Completed 401 Unauthorized in 2ms (Views: 0.2ms | ActiveRecord: 0.0ms)
As can be seen from server logs the ApiApp is correctly returing an error json however in DataApp's I am seeing following error:
no implicit conversion of Array into Hash
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/relation.rb:15:in `merge'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/relation.rb:15:in `apply_to'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:26:in `initialize'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:39:in `new'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:39:in `block in initialize_collection'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:35:in `each'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:35:in `map'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:35:in `initialize_collection'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/attributes.rb:167:in `new_collection'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/relation.rb:71:in `block in fetch'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/http.rb:59:in `request'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/relation.rb:70:in `fetch'
~/.rvm/gems/ruby-2.2.1@data-app/gems/her-0.7.3/lib/her/model/relation.rb:45:in `method_missing'
~/data_app/app/controllers/api_app/clients_controller.rb:7:in `index'
I tried a lot searching solutions for it but it seems there is something basic missing which is causing this error? Do I need to send the JSON from ApiApp in a pre-defined format which Her can understand? I did tried changing my Her model in DataApp to following however it shown me some other error:
module ApiApp
class Client
include Her::Model
parse_root_in_json false, format: :json_api
end
end
Can anybody please let me know what is the root cause behind the issue I am facing and what's the solution for getting rid of it?
Thanks, Jiggneshh