diff --git a/.gitignore b/.gitignore index 3484838a..11afb9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ /workflows /docs/mvp +/scratchpad diff --git a/AGENTS.md b/AGENTS.md index 5ff45406..5a9eba9d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -159,6 +159,12 @@ This Rails app uses a small set of preferred libraries for common integration wo - Prefer duck typing: focus on behavior rather than names. Ask what the object can do and which methods it responds to; prefer ability over identity. - Reasoning: Duck typing is meritocratic. New classes can participate by exposing the right behavior without forcing central dispatch code to learn every subtype. +## Scratchpad + +- `scratchpad/` is for quick test scripts, one-off experiments, and prototyping — anything outside the main implementation code and test suite. +- The directory is gitignored. Do not place production code there. +- Use it when you need to verify something quickly (e.g., hitting an API, testing a query) without writing a proper test. + ## Scope - Apply these as defaults for new work. diff --git a/app/lib/r3x/client/prometheus.rb b/app/lib/r3x/client/prometheus.rb new file mode 100644 index 00000000..26e3fe5a --- /dev/null +++ b/app/lib/r3x/client/prometheus.rb @@ -0,0 +1,23 @@ +module R3x + module Client + class Prometheus + def initialize(base_url:) + @connection = Faraday.new(url: base_url) do |f| + f.request :json + f.response :json + end + end + + def query(promql) + response = connection.get("api/v1/query", query: promql) + raise "Prometheus query failed: #{response.status}" unless response.success? + + Result.new(response.body["data"]) + end + + private + + attr_reader :connection + end + end +end diff --git a/app/lib/r3x/client/prometheus/result.rb b/app/lib/r3x/client/prometheus/result.rb new file mode 100644 index 00000000..9ecc3c9f --- /dev/null +++ b/app/lib/r3x/client/prometheus/result.rb @@ -0,0 +1,38 @@ +module R3x + module Client + class Prometheus + class Result + include Enumerable + + attr_reader :result_type + + def initialize(data) + @result_type = data["resultType"] + @series = data.fetch("result", []).map { |s| Series.new(s) } + end + + def each(&block) + series.each(&block) + end + + private + + attr_reader :series + + Series = Struct.new(:data) do + def metric + data["metric"] + end + + def value + data.dig("value", 1) + end + + def timestamp + data.dig("value", 0) + end + end + end + end + end +end