Class: Flipper::Adapters::Http

Inherits:
Object
  • Object
show all
Includes:
Flipper::Adapter
Defined in:
lib/flipper/adapters/http.rb,
lib/flipper/adapters/http/error.rb,
lib/flipper/adapters/http/client.rb

Defined Under Namespace

Classes: Client, Error

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Flipper::Adapter

#adapter_stack, #default_config, #export, included, #name, #read_only?

Constructor Details

#initialize(options = {}) ⇒ Http

Returns a new instance of Http.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/flipper/adapters/http.rb', line 15

def initialize(options = {})
  @client = Client.new(url: options.fetch(:url),
                       headers: options[:headers],
                       basic_auth_username: options[:basic_auth_username],
                       basic_auth_password: options[:basic_auth_password],
                       read_timeout: options[:read_timeout],
                       open_timeout: options[:open_timeout],
                       write_timeout: options[:write_timeout],
                       max_retries: options[:max_retries],
                       debug_output: options[:debug_output])
  @last_get_all_etag = nil
  @last_get_all_result = nil
  @last_get_all_response = nil
  @get_all_mutex = Mutex.new
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



13
14
15
# File 'lib/flipper/adapters/http.rb', line 13

def client
  @client
end

Instance Method Details

#add(feature) ⇒ Object

Raises:



120
121
122
123
124
125
# File 'lib/flipper/adapters/http.rb', line 120

def add(feature)
  body = JSON.generate(name: feature.key)
  response = @client.post('/features', body)
  raise Error, response unless response.is_a?(Net::HTTPOK)
  true
end

#clear(feature) ⇒ Object

Raises:



154
155
156
157
158
# File 'lib/flipper/adapters/http.rb', line 154

def clear(feature)
  response = @client.delete("/features/#{feature.key}/clear")
  raise Error, response unless response.is_a?(Net::HTTPNoContent)
  true
end

#disable(feature, gate, thing) ⇒ Object

Raises:



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/flipper/adapters/http.rb', line 141

def disable(feature, gate, thing)
  body = request_body_for_gate(gate, thing.value)
  query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : ""
  response = case gate.key
  when :percentage_of_actors, :percentage_of_time
    @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
  else
    @client.delete("/features/#{feature.key}/#{gate.key}#{query_string}", body)
  end
  raise Error, response unless response.is_a?(Net::HTTPOK)
  true
end

#enable(feature, gate, thing) ⇒ Object

Raises:



133
134
135
136
137
138
139
# File 'lib/flipper/adapters/http.rb', line 133

def enable(feature, gate, thing)
  body = request_body_for_gate(gate, thing.value)
  query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : ""
  response = @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
  raise Error, response unless response.is_a?(Net::HTTPOK)
  true
end

#featuresObject

Raises:



112
113
114
115
116
117
118
# File 'lib/flipper/adapters/http.rb', line 112

def features
  response = @client.get('/features?exclude_gate_names=true')
  raise Error, response unless response.is_a?(Net::HTTPOK)

  parsed_response = Typecast.from_json(response.body)
  parsed_response['features'].map { |feature| feature['key'] }.to_set
end

#get(feature) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/flipper/adapters/http.rb', line 31

def get(feature)
  response = @client.get("/features/#{feature.key}")
  if response.is_a?(Net::HTTPOK)
    parsed_response = Typecast.from_json(response.body)
    result_for_feature(feature, parsed_response.fetch('gates'))
  elsif response.is_a?(Net::HTTPNotFound)
    default_config
  else
    raise Error, response
  end
end

#get_all(cache_bust: false) ⇒ Object

Raises:



62
63
64
65
66
67
68
69
70
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
97
98
99
100
101
102
103
104
105
106
# File 'lib/flipper/adapters/http.rb', line 62

def get_all(cache_bust: false)
  options = {}
  path = "/features?exclude_gate_names=true"
  path += "&_cb=#{Time.now.to_i}" if cache_bust
  etag = @get_all_mutex.synchronize { @last_get_all_etag }

  if etag && !cache_bust
    options[:headers] = { if_none_match: etag }
  end

  response = @client.get(path, options)
  @get_all_mutex.synchronize { @last_get_all_response = response }

  if response.is_a?(Net::HTTPNotModified)
    cached_result = @get_all_mutex.synchronize { @last_get_all_result }

    if cached_result
      return cached_result
    else
      raise Error, response
    end
  end

  raise Error, response unless response.is_a?(Net::HTTPOK)

  parsed_response = response.body.empty? ? {} : Typecast.from_json(response.body)
  parsed_features = parsed_response['features'] || []
  gates_by_key = parsed_features.each_with_object({}) do |parsed_feature, hash|
    hash[parsed_feature['key']] = parsed_feature['gates']
    hash
  end

  result = {}
  gates_by_key.each_key do |key|
    feature = Feature.new(key, self)
    result[feature.key] = result_for_feature(feature, gates_by_key[feature.key])
  end

  @get_all_mutex.synchronize do
    @last_get_all_etag = response['etag'] if response['etag']
    @last_get_all_result = result
  end

  result
end

#get_multi(features) ⇒ Object

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/flipper/adapters/http.rb', line 43

def get_multi(features)
  csv_keys = features.map(&:key).join(',')
  response = @client.get("/features?keys=#{csv_keys}&exclude_gate_names=true")
  raise Error, response unless response.is_a?(Net::HTTPOK)

  parsed_response = Typecast.from_json(response.body)
  parsed_features = parsed_response.fetch('features')
  gates_by_key = parsed_features.each_with_object({}) do |parsed_feature, hash|
    hash[parsed_feature['key']] = parsed_feature['gates']
    hash
  end

  result = {}
  features.each do |feature|
    result[feature.key] = result_for_feature(feature, gates_by_key[feature.key])
  end
  result
end

#import(source) ⇒ Object

Raises:



160
161
162
163
164
165
166
# File 'lib/flipper/adapters/http.rb', line 160

def import(source)
  adapter = self.class.from(source)
  export = adapter.export(format: :json, version: 1)
  response = @client.post("/import", export.contents)
  raise Error, response unless response.is_a?(Net::HTTPNoContent)
  true
end

#last_get_all_responseObject



108
109
110
# File 'lib/flipper/adapters/http.rb', line 108

def last_get_all_response
  @get_all_mutex.synchronize { @last_get_all_response }
end

#remove(feature) ⇒ Object

Raises:



127
128
129
130
131
# File 'lib/flipper/adapters/http.rb', line 127

def remove(feature)
  response = @client.delete("/features/#{feature.key}")
  raise Error, response unless response.is_a?(Net::HTTPNoContent)
  true
end