Module: FeatureFlagMonitor

Defined in:
lib/feature_flag_monitor.rb,
lib/feature_flag_monitor/utils.rb,
lib/feature_flag_monitor/version.rb,
lib/feature_flag_monitor/consul_key.rb,
lib/feature_flag_monitor/refinements.rb,
lib/feature_flag_monitor/test/helpers.rb,
lib/feature_flag_monitor/configuration.rb,
lib/feature_flag_monitor/utils/restricted_hash.rb

Defined Under Namespace

Modules: Configuration, Refinements, Test, Utils Classes: ConsulKey, FlagNotFound, GenericError

Constant Summary collapse

IS_A_CNAME =
[String, ->(domain) { domain.property_present?(:cname) }]
IS_AN_ID =
[Numeric, ->(domain) { domain.property_present?(:id) }]
VERSION =
"0.5.4"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.request_idObject

Returns the value of attribute request_id.



17
18
19
# File 'lib/feature_flag_monitor.rb', line 17

def request_id
  @request_id
end

Class Method Details

.bulk_write(flags:, domains: nil, cohort: nil, exceptions: {}, authorization:) ⇒ Object

Raises:

  • (ArgumentError)


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/feature_flag_monitor.rb', line 117

def self.bulk_write(flags:, domains: nil, cohort: nil, exceptions: {}, authorization:)
  raise ArgumentError, 'Must provide either domains or cohort.' if domains.nil? && cohort.nil?

  headers = {
    'X-Socrata-RequestId' => request_id,
    'Content-Type' => 'application/json',
  }.merge(authorization).compact

  payload = {
    flags: flags,
    domains: domains,
    cohort: cohort,
    except: exceptions
  }
  HTTParty.send(:patch, URI.join(FFM, bulk_endpoint), body: payload.to_json, headers: headers).
    tap(&method(:raise_errors_on_non_success))
end

.configure {|Configuration| ... } ⇒ Object

Yields:



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/feature_flag_monitor.rb', line 19

def self.configure
  return if Configuration.done?
  yield(Configuration) if block_given?

  error_msg = lambda do |type|
    lambda do |uri|
      raise Configuration::Error.new("#{type} URI not configured") if uri.nil?
    end
  end

  const_set(:FFM,
    Configuration.feature_flag_monitor_uri.tap(&error_msg.call('Feature Flag Monitor')))
  Diplomat.configure do |config|
    config.url = Configuration.consul_uri.tap(&error_msg.call('Consul'))
  end

  Configuration.done!
end

.domainsObject



59
60
61
62
63
64
# File 'lib/feature_flag_monitor.rb', line 59

def self.domains
  headers = { 'X-Socrata-RequestId' => request_id }.compact
  endpoint = 'domains.json'
  request_store[:domains] ||=
    JSON.parse(HTTParty.get(URI.join(FFM, endpoint), headers: headers).body)
end

.flag(name:, on_domain:) ⇒ Object



80
81
82
# File 'lib/feature_flag_monitor.rb', line 80

def self.flag(name:, on_domain:)
  flags_on(domain: on_domain)[name.to_s]
end

.flags_on(domain:) ⇒ Object



70
71
72
73
74
75
76
77
78
# File 'lib/feature_flag_monitor.rb', line 70

def self.flags_on(domain:)
  request_store[:"flags_on_#{domain}"] ||=
    fetch(
      case domain
      when *IS_A_CNAME then ConsulKey.by_cname(domain.attempt(:cname))
      when *IS_AN_ID   then ConsulKey.by_id(domain.attempt(:id))
      end.for_domain
    )
end

.list(with_descriptions: false) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/feature_flag_monitor.rb', line 41

def self.list(with_descriptions: false)
  request_store[:"list_#{with_descriptions}"] ||=
    case with_descriptions
    when TrueClass
      headers = { 'X-Socrata-RequestId' => request_id }.compact
      JSON.parse(HTTParty.get(URI.join(FFM, 'describe.json'), headers: headers).body)
    when FalseClass
      type_data.map_values { |type| { 'type' => type } }
    end.map_values do |config|
      # Entirely for backwards compatibility with Signaller's config format.
      config['disableTrueFalse'] = true unless config['type'].include?('boolean')
      (config['type'] - [ 'boolean', 'integer', 'string' ]).tap do |expected_values|
        config['expectedValues'] = expected_values.join(' ') unless expected_values.empty?
      end
      config
    end
end

.report(for_flag:) ⇒ Object



84
85
86
87
88
89
# File 'lib/feature_flag_monitor.rb', line 84

def self.report(for_flag:)
  headers = { 'X-Socrata-RequestId' => request_id }.compact
  endpoint = [ 'report', for_flag ].join('/') << '.json'
  request_store[:"report_#{for_flag}"] ||=
    JSON.parse(HTTParty.get(URI.join(FFM, endpoint), headers: headers).body)
end

.reset(flag:, domain: nil, authorization:) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/feature_flag_monitor.rb', line 135

def self.reset(flag:, domain: nil, authorization:)
  domain = extract_cname(domain)
  headers = {
    'X-Socrata-Host' => domain,
    'X-Socrata-RequestId' => request_id,
    'Content-Type' => 'application/json',
  }.merge(authorization).compact

  endpoint = flag_endpoint(flag, domain)
  HTTParty.delete(URI.join(FFM, endpoint), headers: headers).
    tap(&method(:raise_errors_on_non_success))
end

.set(flag:, value:, domain: nil, authorization:) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/feature_flag_monitor.rb', line 91

def self.set(flag:, value:, domain: nil, authorization:)
  domain = extract_cname(domain)
  headers = {
    'X-Socrata-Host' => domain,
    'X-Socrata-RequestId' => request_id,
    'Content-Type' => 'application/json',
  }.merge(authorization).compact

  endpoint = flag_endpoint(flag, domain)
  HTTParty.put(URI.join(FFM, endpoint), body: value.to_json, headers: headers).
    tap(&method(:raise_errors_on_non_success))
end

.set_multiple(flags:, domain: nil, authorization:) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/feature_flag_monitor.rb', line 104

def self.set_multiple(flags:, domain: nil, authorization:)
  domain = extract_cname(domain)
  headers = {
    'X-Socrata-Host' => domain,
    'X-Socrata-RequestId' => request_id,
    'Content-Type' => 'application/json',
  }.merge(authorization).compact

  endpoint = domain_endpoint(domain)
  HTTParty.send(:patch, URI.join(FFM, endpoint), body: flags.to_json, headers: headers).
    tap(&method(:raise_errors_on_non_success))
end

.type_dataObject



66
67
68
# File 'lib/feature_flag_monitor.rb', line 66

def self.type_data
  request_store[:type_data] ||= fetch(ConsulKey.for_types)
end