Class: BlueStateDigital::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/blue_state_digital/connection.rb

Constant Summary collapse

API_VERSION =
2
API_BASE =
'/page/api'
GRAPH_API_BASE =
'/page/graph'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Connection

Returns a new instance of Connection.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/blue_state_digital/connection.rb', line 13

def initialize(params = {})
  @api_id = params[:api_id]
  @api_secret = params[:api_secret]
  self.instrumentation = params[:instrumentation]

  @client = Faraday.new(:url => "https://#{params[:host]}/") do |faraday|
    faraday.request  :url_encoded             # form-encode POST params
    if defined?(Rails) && Rails.env.development?
      faraday.response :logger                  # log requests to STDOUT
    end
    faraday.response :error_middleware
    faraday.adapter(params[:adapter] || Faraday.default_adapter)  # make requests with Net::HTTP by default
  end
  set_up_resources
end

Instance Attribute Details

#constituent_groupsObject (readonly)

Returns the value of attribute constituent_groups.



9
10
11
# File 'lib/blue_state_digital/connection.rb', line 9

def constituent_groups
  @constituent_groups
end

#constituentsObject (readonly)

Returns the value of attribute constituents.



9
10
11
# File 'lib/blue_state_digital/connection.rb', line 9

def constituents
  @constituents
end

#dataset_mapsObject (readonly)

Returns the value of attribute dataset_maps.



9
10
11
# File 'lib/blue_state_digital/connection.rb', line 9

def dataset_maps
  @dataset_maps
end

#datasetsObject (readonly)

Returns the value of attribute datasets.



9
10
11
# File 'lib/blue_state_digital/connection.rb', line 9

def datasets
  @datasets
end

#instrumentationObject

Returns the value of attribute instrumentation.



11
12
13
# File 'lib/blue_state_digital/connection.rb', line 11

def instrumentation
  @instrumentation
end

Instance Method Details

#compute_hmac(path, api_ts, params) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/blue_state_digital/connection.rb', line 71

def compute_hmac(path, api_ts, params)
  # Support Faraday 0.9.0 forward
  # Faraday now normalizes request parameters via sorting by default but also allows
  # the params encoder to be configured by client.  It includes Faraday::NestedParamsEncoder
  # and Faraday::FlatParamsEncoder, but a 3rd party one can be provided.
  #
  # When computing the hmac, we need to normalize/sort the exact same way.

   if Faraday::VERSION == "0.8.9"
     # do it the old way
     canon_params= params.map { |k, v| "#{k.to_s}=#{v.to_s}" }.join('&')

   else  # 0.9.0+ do it the new way

     # Find out which one is in use or select default
     params_encoder = @client.options[:params_encoder] || Faraday::Utils.default_params_encoder

     # Call that params_encoder when creating signing string. Note we must unescape for BSD
     canon_params = URI.decode_www_form_component(params_encoder.encode(params))

   end
   signing_string = [@api_id, api_ts, path, canon_params].join("\n")

   OpenSSL::HMAC.hexdigest('sha1', @api_secret, signing_string)

end

#extended_params(path, params) ⇒ Object



98
99
100
101
102
103
# File 'lib/blue_state_digital/connection.rb', line 98

def extended_params(path, params)
  api_ts = Time.now.utc.to_i.to_s
  extended_params = { api_ver: API_VERSION, api_id: @api_id, api_ts: api_ts }.merge(params)
  extended_params[:api_mac] = compute_hmac(path, api_ts, extended_params)
  extended_params
end

#get_deferred_results(deferred_id) ⇒ Object



112
113
114
# File 'lib/blue_state_digital/connection.rb', line 112

def get_deferred_results(deferred_id)
  perform_request '/get_deferred_results', {deferred_id: deferred_id}, "GET"
end

#perform_graph_request(call, params, method = 'POST') ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/blue_state_digital/connection.rb', line 61

def perform_graph_request(call, params, method = 'POST')
  path = GRAPH_API_BASE + call

  if method == "POST"
    @client.post do |req|
      req.url(path, params)
    end
  end
end

#perform_request(call, params = {}, method = "GET", body = nil) ⇒ Object



29
30
31
# File 'lib/blue_state_digital/connection.rb', line 29

def perform_request(call, params = {}, method = "GET", body = nil)
  perform_request_raw(call,params,method,body).body
end

#perform_request_raw(call, params = {}, method = "GET", body = nil) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/blue_state_digital/connection.rb', line 33

def perform_request_raw(call, params = {}, method = "GET", body = nil)
  path = API_BASE + call

  # instrumentation is a Proc that is called for each request that is performed.
  if self.instrumentation
    stats = {}
    stats[:path] = path
    stats[:api_id] = @api_id
    self.instrumentation.call(stats)
  end

  resp = if method == "POST" || method == "PUT"
    @client.send(method.downcase.to_sym) do |req|
      content_type = params.delete(:content_type) || 'application/x-www-form-urlencoded'
      accept = params.delete(:accept) || 'text/xml'
      req.url(path, extended_params(path, params))
      req.body = body
      req.options.timeout = 120
      req.headers['Content-Type'] = content_type
      req.headers['Accept'] = accept
    end
  else
    @client.get(path, extended_params(path, params))
  end

  resp
end

#retrieve_results(deferred_id) ⇒ Object



116
117
118
119
120
121
122
123
124
# File 'lib/blue_state_digital/connection.rb', line 116

def retrieve_results(deferred_id)
  begin
    return get_deferred_results(deferred_id)
  rescue Faraday::Error::ClientError => e
    if e.response[:status] == 503
      return nil
    end
  end
end

#set_up_resourcesObject

:doc:



105
106
107
108
109
110
# File 'lib/blue_state_digital/connection.rb', line 105

def set_up_resources # :doc:
   @constituents = BlueStateDigital::Constituents.new(self)
   @constituent_groups = BlueStateDigital::ConstituentGroups.new(self)
   @datasets = BlueStateDigital::Datasets.new(self)
   @dataset_maps = BlueStateDigital::DatasetMaps.new(self)
end

#wait_for_deferred_result(deferred_id, timeout = 600) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/blue_state_digital/connection.rb', line 126

def wait_for_deferred_result(deferred_id, timeout = 600)
  result = nil
  time_waiting = 0
  while result.nil? || (result.respond_to?(:length) && result.length == 0)
    result = retrieve_results(deferred_id)
    if result.nil? || (result.respond_to?(:length) && result.length == 0)
      time_waiting = time_waiting + 2
      if time_waiting > timeout
        raise BlueStateDigital::DeferredResultTimeout.new("exceeded timeout #{timeout} seconds waiting for #{deferred_id}")
      end
      sleep(2)
    end
  end
  result
end