Module: Quonfig::Datadir

Defined in:
lib/quonfig/datadir.rb

Overview

Loads a Quonfig workspace from the local filesystem (offline / datadir mode). Mirrors sdk-node/src/datadir.ts.

The workspace directory layout matches integration-test-data:

<datadir>/quonfig.json
<datadir>/configs/*.json
<datadir>/feature-flags/*.json
<datadir>/segments/*.json
<datadir>/schemas/*.json
<datadir>/log-levels/*.json

Each <type>/*.json file is a WorkspaceConfigDocument. The loader projects it down to the ConfigResponse shape that the SSE/HTTP delivery path emits, so ConfigStore consumes both transports uniformly.

Constant Summary collapse

CONFIG_SUBDIRS =
%w[configs feature-flags segments schemas log-levels].freeze

Class Method Summary collapse

Class Method Details

.effective_send_to_client_sdk(type, raw) ⇒ Object



95
96
97
98
99
# File 'lib/quonfig/datadir.rb', line 95

def effective_send_to_client_sdk(type, raw)
  return true if type == 'feature_flag'

  raw || false
end

.load_envelope(datadir, environment = nil) ⇒ Object

Read every config JSON in ‘datadir`, project to ConfigResponse hashes, and wrap in a ConfigEnvelope. Does no network I/O.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/quonfig/datadir.rb', line 27

def load_envelope(datadir, environment = nil)
  env_id = resolve_environment(File.join(datadir, 'quonfig.json'), environment)
  configs = []

  CONFIG_SUBDIRS.each do |subdir|
    dir = File.join(datadir, subdir)
    next unless Dir.exist?(dir)

    Dir.children(dir)
       .select { |name| name.end_with?('.json') }
       .sort
       .each do |filename|
      raw = JSON.parse(File.read(File.join(dir, filename)))
      configs << to_config_response(raw, env_id)
    end
  end

  Quonfig::ConfigEnvelope.new(
    configs: configs,
    meta: { 'version' => "datadir:#{datadir}", 'environment' => env_id }
  )
end

.load_store(datadir, environment = nil) ⇒ Object

Convenience: load the envelope and populate a fresh ConfigStore.



51
52
53
54
55
56
# File 'lib/quonfig/datadir.rb', line 51

def load_store(datadir, environment = nil)
  envelope = load_envelope(datadir, environment)
  store = Quonfig::ConfigStore.new
  envelope.configs.each { |cfg| store.set(cfg['key'], cfg) }
  store
end

.resolve_environment(quonfig_path, environment) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/quonfig/datadir.rb', line 58

def resolve_environment(quonfig_path, environment)
  environment ||= ENV['QUONFIG_ENVIRONMENT']

  if environment.nil? || environment.empty?
    raise ArgumentError,
          '[quonfig] Environment required for datadir mode; set the `environment` option or QUONFIG_ENVIRONMENT env var'
  end

  unless File.exist?(quonfig_path)
    raise ArgumentError, "[quonfig] Datadir is missing quonfig.json: #{quonfig_path}"
  end

  environments = JSON.parse(File.read(quonfig_path)).fetch('environments', [])

  if !environments.empty? && !environments.include?(environment)
    raise ArgumentError,
          "[quonfig] Environment \"#{environment}\" not found in workspace; available environments: #{environments.join(', ')}"
  end

  environment
end

.to_config_response(raw, env_id) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/quonfig/datadir.rb', line 80

def to_config_response(raw, env_id)
  environment = Array(raw['environments']).find { |e| e['id'] == env_id }
  type = raw['type']

  {
    'id' => raw['id'] || '',
    'key' => raw['key'],
    'type' => type,
    'valueType' => raw['valueType'],
    'sendToClientSdk' => effective_send_to_client_sdk(type, raw['sendToClientSdk']),
    'default' => raw['default'] || { 'rules' => [] },
    'environment' => environment
  }
end