Class: FluidFeatures::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/fluidfeatures/client.rb

Constant Summary collapse

API_REQUEST_LOG_MAX_SIZE =
200
MIN_GZIP_SIZE =

Do not gzip request or response body if size is under N bytes

1024

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_uri, logger) ⇒ Client

Returns a new instance of Client.

Raises:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/fluidfeatures/client.rb', line 22

def initialize(base_uri, logger)

  raise FFeaturesBadParam.new("base_uri is not String, is #{base_uri.class}") \
    unless base_uri.is_a? String

  @uuid = UUID.new.generate
  @logger = logger
  @base_uri = base_uri

  @http = ::PersistentHTTP.new(
    :name         => 'fluidfeatures',
    :logger       => logger,
    :pool_size    => 10,
    :warn_timeout => 0.25,
    :force_retry  => true,
    :url          => base_uri
  )

  @api_request_log = []
  @api_request_log_lock = ::Mutex.new

  @etags = {}
  @etags_lock = ::Mutex.new

end

Instance Attribute Details

#base_uriObject

Returns the value of attribute base_uri.



15
16
17
# File 'lib/fluidfeatures/client.rb', line 15

def base_uri
  @base_uri
end

#loggerObject

Returns the value of attribute logger.



15
16
17
# File 'lib/fluidfeatures/client.rb', line 15

def logger
  @logger
end

#uuidObject

Returns the value of attribute uuid.



15
16
17
# File 'lib/fluidfeatures/client.rb', line 15

def uuid
  @uuid
end

Instance Method Details

#encode_request_body(request, payload, encoding = "gzip") ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/fluidfeatures/client.rb', line 240

def encode_request_body(request, payload, encoding="gzip")

  # Encode as JSON string
  content = JSON.dump(payload)

  # Gzip compress if necessary
  if encoding == "gzip" and content.size >= MIN_GZIP_SIZE
    compressed = StringIO.new
    gz_writer = Zlib::GzipWriter.new(compressed)
    gz_writer.write(content)
    gz_writer.close
    content = compressed.string
    request["Content-Encoding"] = encoding
  end

  request["Content-Type"] = "application/json"
  request.body = content

end

#get(path, auth_token, url_params = nil, cache = false) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/fluidfeatures/client.rb', line 74

def get(path, auth_token, url_params=nil, cache=false)
  payload = nil

  uri = URI(@base_uri + path)
  url_path = uri.path
  if url_params
    uri.query = URI.encode_www_form( url_params )
    if uri.query
      url_path += "?" + uri.query
    end
  end

  duration = nil
  status_code = nil
  err_msg = nil
  no_change = false
  success = false
  begin

    request = Net::HTTP::Get.new url_path
    request["Authorization"] = auth_token
    request["Accept"] = "application/json"
    request["Accept-Encoding"] = "gzip"

    @etags_lock.synchronize do
      if cache and @etags.has_key? url_path
        request["If-None-Match"] = @etags[url_path][:etag]
      end
    end

    request_start_time = Time.now
    response = @http.request request
    duration = Time.now - request_start_time

    if response.is_a? Net::HTTPResponse
      status_code = response.code
      if response.is_a? Net::HTTPNotModified
        no_change = true
        success = true
      elsif response.is_a? Net::HTTPSuccess
        payload = parse_response_body response
        if cache
          @etags_lock.synchronize do
            @etags[url_path] = {
              :etag => response["Etag"],
              :time => Time.now
            }
          end
        end
        success = true
      else
        payload = parse_response_body response
        if payload and payload.is_a? Hash and payload.has_key? "error"
          err_msg = payload["error"]
        end
        logger.error{"[FF] Request unsuccessful for GET #{path} : #{response.class} #{status_code} #{err_msg}"}
      end
    end
  rescue PersistentHTTP::Error => err
    logger.error{"[FF] Request failed for GET #{path} : #{err.message}"}
  rescue
    logger.error{"[FF] Request failed for GET #{path} : #{status_code} #{err_msg}"}
    raise
  else
    unless no_change or payload
      logger.error{"[FF] Empty response for GET #{path} : #{status_code} #{err_msg}"}
    end
  end

  log_api_request("GET", url_path, duration, status_code, err_msg)

  return success, payload
end

#log_api_request(method, url, duration, status_code, err_msg) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/fluidfeatures/client.rb', line 48

def log_api_request(method, url, duration, status_code, err_msg)
  @api_request_log_lock.synchronize do
    @api_request_log << {
      :method => method,
      :url => url,
      :duration => duration,
      :status => status_code,
      :err => err_msg,
      :time => Time.now.to_f.round(2)
    }
    # remove older entry if too big
    if @api_request_log.size > API_REQUEST_LOG_MAX_SIZE
      @api_request_log.shift
    end
  end
end

#parse_response_body(response) ⇒ Object



230
231
232
233
234
235
236
237
238
# File 'lib/fluidfeatures/client.rb', line 230

def parse_response_body response
  content = response.body
  if response["Content-Encoding"] == "gzip"
    content = Zlib::GzipReader.new(
      StringIO.new(content)
    ).read
  end
  JSON.load(content) rescue nil
end

#post(path, auth_token, payload) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/fluidfeatures/client.rb', line 189

def post(path, auth_token, payload)
  uri = URI(@base_uri + path)
  url_path = uri.path
  duration = nil
  status_code = nil
  err_msg = nil
  success = false
  begin

    request = Net::HTTP::Post.new url_path
    request["Accept"] = "application/json"
    request["Accept-Encoding"] = "gzip"
    request["Authorization"] = auth_token
    encode_request_body(request, payload)

    request_start_time = Time.now
    response = @http.request request
    duration = Time.now - request_start_time

    raise "expected Net::HTTPResponse" if not response.is_a? Net::HTTPResponse
    status_code = response.code
    if response.is_a? Net::HTTPSuccess
      success = true
    else
      response_payload = parse_response_body response
      if response_payload.is_a? Hash and response_payload.has_key? "error"
        err_msg = response_payload["error"]
      end
      logger.error{"[FF] Request unsuccessful for POST #{path} : #{status_code} #{err_msg}"}
    end
  rescue PersistentHTTP::Error => err
    logger.error{"[FF] Request failed for POST #{path} : #{err.message}"}
  rescue Exception => err
    logger.error{"[FF] Request failed for POST #{path} : #{err.message}"}
    raise
  end

  log_api_request("POST", url_path, duration, status_code, err_msg)
  return success
end

#put(path, auth_token, payload) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/fluidfeatures/client.rb', line 148

def put(path, auth_token, payload)
  uri = URI(@base_uri + path)
  url_path = uri.path
  duration = nil
  status_code = nil
  err_msg = nil
  success = false
  begin

    request = Net::HTTP::Put.new url_path
    request["Authorization"] = auth_token
    request["Accept"] = "application/json"
    request["Accept-Encoding"] = "gzip"
    encode_request_body(request, payload)

    request_start_time = Time.now
    response = @http.request request
    duration = Time.now - request_start_time

    raise "expected Net::HTTPResponse" if not response.is_a? Net::HTTPResponse
    status_code = response.code
    if response.is_a? Net::HTTPSuccess
      success = true
    else
      response_payload = parse_response_body response
      if response_payload.is_a? Hash and response_payload.has_key? "error"
        err_msg = response_payload["error"]
      end
      logger.error{"[FF] Request unsuccessful for PUT #{path} : #{status_code} #{err_msg}"}
    end
  rescue PersistentHTTP::Error => err
    logger.error{"[FF] Request failed for PUT #{path} : #{err.message}"}
  rescue Exception => err
    logger.error{"[FF] Request failed for PUT #{path} : #{err.message}"}
    raise
  end

  log_api_request("PUT", url_path, duration, status_code, err_msg)
  return success
end

#siphon_api_request_logObject



65
66
67
68
69
70
71
72
# File 'lib/fluidfeatures/client.rb', line 65

def siphon_api_request_log
  request_log = nil
  @api_request_log_lock.synchronize do
    request_log = @api_request_log
    @api_request_log = []
  end
  request_log
end