Class: Shipeasy::SDK::FlagsClient

Inherits:
Object
  • Object
show all
Defined in:
lib/shipeasy/sdk/flags_client.rb

Constant Summary collapse

DEFAULT_BASE_URL =
"https://edge.shipeasy.dev"

Instance Method Summary collapse

Constructor Details

#initialize(api_key:, base_url: nil, env: "prod", disable_telemetry: false, telemetry_url: nil) ⇒ FlagsClient

Returns a new instance of FlagsClient.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/shipeasy/sdk/flags_client.rb', line 14

def initialize(api_key:, base_url: nil, env: "prod", disable_telemetry: false, telemetry_url: nil)
  @api_key     = api_key
  @base_url    = (base_url || DEFAULT_BASE_URL).chomp("/")
  # Per-evaluation usage telemetry. ON by default; pass
  # disable_telemetry: true to opt out. See telemetry.rb.
  @telemetry = Telemetry.new(
    endpoint: telemetry_url || Telemetry::DEFAULT_TELEMETRY_URL,
    sdk_key: api_key,
    side: "server",
    env: env,
    disabled: disable_telemetry,
  )
  @flags_blob  = nil
  @exps_blob   = nil
  @flags_etag  = nil
  @exps_etag   = nil
  @poll_interval = 30
  @mutex       = Mutex.new
  @timer       = nil
  @initialized = false
end

Instance Method Details

#destroyObject



48
49
50
51
# File 'lib/shipeasy/sdk/flags_client.rb', line 48

def destroy
  @timer&.kill
  @timer = nil
end

#get_config(name, decode = nil) ⇒ Object



60
61
62
63
64
65
66
# File 'lib/shipeasy/sdk/flags_client.rb', line 60

def get_config(name, decode = nil)
  @telemetry.emit("config", name)
  entry = @mutex.synchronize { @flags_blob&.dig("configs", name) }
  return nil unless entry
  value = entry["value"]
  decode ? decode.call(value) : value
end

#get_experiment(name, user, default_params, decode = nil) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/shipeasy/sdk/flags_client.rb', line 68

def get_experiment(name, user, default_params, decode = nil)
  @telemetry.emit("experiment", name)
  flags_blob, exps_blob = @mutex.synchronize { [@flags_blob, @exps_blob] }
  exp = exps_blob&.dig("experiments", name)
  result = Eval.eval_experiment(exp, flags_blob, exps_blob, with_anon_id(user))
  result.params ||= default_params

  if result.in_experiment && decode
    begin
      result = Eval::ExperimentResult.new(
        in_experiment: true,
        group: result.group,
        params: decode.call(result.params),
      )
    rescue => e
      warn "[shipeasy] get_experiment('#{name}') decode failed: #{e.message}"
      return Eval::ExperimentResult.new(in_experiment: false, group: "control", params: default_params)
    end
  end

  result
end

#get_flag(name, user) ⇒ Object



53
54
55
56
57
58
# File 'lib/shipeasy/sdk/flags_client.rb', line 53

def get_flag(name, user)
  @telemetry.emit("gate", name)
  gate = @mutex.synchronize { @flags_blob&.dig("gates", name) }
  return false unless gate
  Eval.eval_gate(gate, with_anon_id(user))
end

#initObject



36
37
38
39
40
# File 'lib/shipeasy/sdk/flags_client.rb', line 36

def init
  fetch_all
  @initialized = true
  start_poll
end

#init_onceObject



42
43
44
45
46
# File 'lib/shipeasy/sdk/flags_client.rb', line 42

def init_once
  return if @initialized
  fetch_all
  @initialized = true
end

#track(user_id, event_name, props = {}) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/shipeasy/sdk/flags_client.rb', line 91

def track(user_id, event_name, props = {})
  payload = JSON.generate({
    events: [{
      type: "metric",
      event_name: event_name,
      user_id: user_id.to_s,
      ts: (Time.now.to_f * 1000).to_i,
      **(props.empty? ? {} : { properties: props }),
    }],
  })

  Thread.new do
    post("/collect", payload)
  rescue => e
    warn "[shipeasy] track failed: #{e.message}"
  end
end