Module: Legion::API::Helpers

Defined in:
lib/legion/api/acp.rb,
lib/legion/api/helpers.rb

Defined Under Namespace

Modules: Acp

Instance Method Summary collapse

Instance Method Details

#authenticated?Boolean

Returns:

  • (Boolean)


202
203
204
# File 'lib/legion/api/helpers.rb', line 202

def authenticated?
  !current_claims.nil?
end

#build_schedule_attrs(body) ⇒ Object



168
169
170
171
172
173
174
175
176
# File 'lib/legion/api/helpers.rb', line 168

def build_schedule_attrs(body)
  attrs = { function_id: body[:function_id].to_i, active: body.fetch(:active, true), last_run: Time.at(0) }
  attrs[:cron] = body[:cron] if body[:cron]
  attrs[:interval] = body[:interval].to_i if body[:interval]
  attrs[:task_ttl] = body[:task_ttl].to_i if body[:task_ttl]
  attrs[:payload] = Legion::JSON.dump(body[:payload] || {})
  attrs[:transformation] = body[:transformation] if body[:transformation]
  attrs
end

#build_schedule_updates(body) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
# File 'lib/legion/api/helpers.rb', line 178

def build_schedule_updates(body)
  updates = {}
  updates[:cron] = body[:cron] if body.key?(:cron)
  updates[:interval] = body[:interval].to_i if body.key?(:interval)
  updates[:active] = body[:active] if body.key?(:active)
  updates[:task_ttl] = body[:task_ttl].to_i if body.key?(:task_ttl)
  updates[:function_id] = body[:function_id].to_i if body.key?(:function_id)
  updates[:payload] = Legion::JSON.dump(body[:payload]) if body.key?(:payload)
  updates[:transformation] = body[:transformation] if body.key?(:transformation)
  updates
end

#current_claimsObject



190
191
192
# File 'lib/legion/api/helpers.rb', line 190

def current_claims
  env['legion.auth']
end

#current_owner_msidObject



198
199
200
# File 'lib/legion/api/helpers.rb', line 198

def current_owner_msid
  env['legion.owner_msid']
end

#current_worker_idObject



194
195
196
# File 'lib/legion/api/helpers.rb', line 194

def current_worker_id
  env['legion.worker_id']
end

#find_extension_module(lex_name) ⇒ Object



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

def find_extension_module(lex_name)
  short = lex_name.delete_prefix('lex-')
  short_no_sep = short.tr('-', '_').delete('_')
  Legion::Extensions.loaded_extension_modules.find do |mod|
    parts = mod.name&.split('::')
    mod_short = parts&.last&.downcase
    mod_short == short.tr('-', '_') ||
      mod_short == short.delete('-') ||
      mod_short == short_no_sep
  end
end

#find_or_halt(model_class, id) ⇒ Object



137
138
139
140
141
# File 'lib/legion/api/helpers.rb', line 137

def find_or_halt(model_class, id)
  record = model_class[id.to_i]
  halt 404, json_error('not_found', "#{model_class.name.split('::').last} #{id} not found", status_code: 404) if record.nil?
  record
end

#find_runner_info(ext_mod, runner_name) ⇒ Object



116
117
118
119
120
121
122
# File 'lib/legion/api/helpers.rb', line 116

def find_runner_info(ext_mod, runner_name)
  return nil unless ext_mod.respond_to?(:runners)

  ext_mod.runners.values.find do |r|
    r[:runner_name].to_s.downcase == runner_name.downcase
  end
end

#halt_not_found(message) ⇒ Object



133
134
135
# File 'lib/legion/api/helpers.rb', line 133

def halt_not_found(message)
  halt 404, json_error('not_found', message, status_code: 404)
end

#json_collection(dataset, status_code: 200) ⇒ Object



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

def json_collection(dataset, status_code: 200)
  content_type :json
  status status_code

  paginated = paginate(dataset)
  items = paginated.respond_to?(:all) ? paginated.all : Array(paginated)
  total = collection_total(dataset, items)
  meta = response_meta.merge(
    count:  items.length,
    limit:  page_limit,
    offset: page_offset
  )
  meta[:total] = total unless total.nil?
  meta[:has_more] = collection_has_more?(items, total)

  Legion::JSON.dump({
                      data: items.map { |r| r.respond_to?(:values) ? r.values : r },
                      meta: meta
                    })
end

#json_error(code, message, status_code: 400) ⇒ Object



36
37
38
39
40
41
42
43
# File 'lib/legion/api/helpers.rb', line 36

def json_error(code, message, status_code: 400)
  content_type :json
  status status_code
  Legion::JSON.dump({
                      error: { code: code, message: message },
                      meta:  response_meta
                    })
end

#json_response(data, status_code: 200) ⇒ Object



6
7
8
9
10
11
12
13
# File 'lib/legion/api/helpers.rb', line 6

def json_response(data, status_code: 200)
  content_type :json
  status status_code
  Legion::JSON.dump({
                      data: data,
                      meta: response_meta
                    })
end

#parse_request_bodyObject



94
95
96
97
98
99
100
101
102
# File 'lib/legion/api/helpers.rb', line 94

def parse_request_body
  body = request.body.read
  return {} if body.nil? || body.empty?

  Legion::JSON.load(body).transform_keys(&:to_sym)
rescue StandardError => e
  Legion::Logging.warn "API#parse_request_body failed to parse JSON: #{e.message}" if defined?(Legion::Logging)
  halt 400, json_error('invalid_json', 'request body is not valid JSON', status_code: 400)
end

#redact_hash(hash, sensitive_keys: %i[password secret token key cert private_key api_key])) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/legion/api/helpers.rb', line 143

def redact_hash(hash, sensitive_keys: %i[password secret token key cert private_key api_key])
  return hash unless hash.is_a?(Hash)

  hash.each_with_object({}) do |(k, v), result|
    key_sym = k.to_sym
    result[k] = if v.is_a?(Hash)
                  redact_hash(v, sensitive_keys: sensitive_keys)
                elsif sensitive_keys.any? { |s| key_sym.to_s.include?(s.to_s) }
                  '[REDACTED]'
                else
                  v
                end
  end
end

#require_data!Object



45
46
47
48
49
# File 'lib/legion/api/helpers.rb', line 45

def require_data!
  return if Legion::Settings[:data][:connected]

  halt 503, json_error('data_unavailable', 'legion-data is not connected', status_code: 503)
end

#require_knowledge_ingest!Object



64
65
66
67
68
# File 'lib/legion/api/helpers.rb', line 64

def require_knowledge_ingest!
  return if defined?(Legion::Extensions::Knowledge::Runners::Ingest)

  halt 503, json_error('knowledge_unavailable', 'lex-knowledge is not loaded', status_code: 503)
end

#require_knowledge_maintenance!Object



70
71
72
73
74
# File 'lib/legion/api/helpers.rb', line 70

def require_knowledge_maintenance!
  return if defined?(Legion::Extensions::Knowledge::Runners::Maintenance)

  halt 503, json_error('knowledge_unavailable', 'lex-knowledge is not loaded', status_code: 503)
end

#require_knowledge_monitor!Object



76
77
78
79
80
# File 'lib/legion/api/helpers.rb', line 76

def require_knowledge_monitor!
  return if defined?(Legion::Extensions::Knowledge::Runners::Monitor)

  halt 503, json_error('knowledge_unavailable', 'lex-knowledge is not loaded', status_code: 503)
end

#require_knowledge_query!Object



58
59
60
61
62
# File 'lib/legion/api/helpers.rb', line 58

def require_knowledge_query!
  return if defined?(Legion::Extensions::Knowledge::Runners::Query)

  halt 503, json_error('knowledge_unavailable', 'lex-knowledge is not loaded', status_code: 503)
end

#require_mesh!Object



82
83
84
85
86
# File 'lib/legion/api/helpers.rb', line 82

def require_mesh!
  return if defined?(Legion::Extensions::Mesh)

  halt 503, json_error('mesh_unavailable', 'lex-mesh is not loaded', status_code: 503)
end

#require_scheduler!Object



51
52
53
54
55
56
# File 'lib/legion/api/helpers.rb', line 51

def require_scheduler!
  require_data!
  return if defined?(Legion::Extensions::Scheduler)

  halt 503, json_error('scheduler_unavailable', 'lex-scheduler is not loaded', status_code: 503)
end

#require_trace_search!Object



88
89
90
91
92
# File 'lib/legion/api/helpers.rb', line 88

def require_trace_search!
  return if defined?(Legion::TraceSearch) && defined?(Legion::LLM)

  halt 503, json_error('trace_search_unavailable', 'TraceSearch requires LLM subsystem', status_code: 503)
end

#runner_summaries(ext_mod) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/legion/api/helpers.rb', line 124

def runner_summaries(ext_mod)
  return [] unless ext_mod.respond_to?(:runners)

  ext_mod.runners.values.map do |r|
    functions = r[:runner_module]&.instance_methods(false)&.map(&:to_s) || []
    { name: r[:runner_name], runner_class: r[:runner_class], functions: functions }
  end
end

#transport_subclasses(base_class) ⇒ Object



158
159
160
161
162
163
164
165
166
# File 'lib/legion/api/helpers.rb', line 158

def transport_subclasses(base_class)
  ObjectSpace.each_object(Class)
             .select { |klass| klass < base_class }
             .map { |klass| { name: klass.name } }
             .sort_by { |h| h[:name].to_s }
rescue NameError => e
  Legion::Logging.debug "API#transport_subclasses failed for #{base_class}: #{e.message}" if defined?(Legion::Logging)
  []
end