exp/api/openapi: add oapi-codegen generated API client PoC - #2080
Open
AmariahAK wants to merge 7 commits into
Open
exp/api/openapi: add oapi-codegen generated API client PoC#2080AmariahAK wants to merge 7 commits into
AmariahAK wants to merge 7 commits into
Conversation
…#1998 Add an experimental OpenAPI-based HTTP API client under exp/api/openapi/ as a proof-of-concept for issue prometheus#1998 (Consider moving to OpenAPI based HTTP API). The package includes: - An OpenAPI 3.0.3 specification covering all 22 Prometheus v1 endpoints - Generated client + types (5,216 lines) produced by oapi-codegen v2.8.0 - A high-level APIClient wrapper with model.Value dispatch (scalar/vector/matrix) - Unit tests covering instant query, range query, and label endpoints - Decode benchmarks: ~95µs (vector 100 series), ~2.5ms (matrix 10×1000) Key findings: oapi-codegen is viable. Main challenges are type fidelity (the query result field cannot be typed as model.Value in OpenAPI) and performance (standard encoding/json is slower than the current json-iterator+unsafe decoders; custom template injection could help). Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
- Add 3 new tests: LabelValues, WarningsAndInfos, QueryWithWarnings - Add histogram decode benchmark (10x100 samples, ~1.2ms, 327KB) - Evaluate real Prometheus OpenAPI 3.1 spec (5,510 lines from golden file): generates 12,739 lines but has ParseQueryResponse name collisions due to shared response types between GET/POST endpoints - Update README with real spec findings, histogram benchmark data, and updated test count (8 tests, 3 benchmarks) Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
client.go and client_test.go were missing the standard Prometheus Apache License 2.0 header required by the CI license compliance check in Makefile.common. Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
The CI license check rejects 'Copyright 2026 The Prometheus Authors' and instead requires 'Copyright The Prometheus Authors' (no year) for files with copyright year 2026 or later. Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
Add concrete resolution steps for the two issues blocking the real OpenAPI 3.1 spec from generating a compilable client: 1. Parse function collisions (duplicate QueryOutputBody refs) 2. Type name mismatches between hand-crafted and real operation IDs Also explain why the PoC ships a hand-crafted spec: the real spec issues are implementable but not blocking for the viability assessment. Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
Add x-go-name extensions to /query GET and POST operations in the real Prometheus OpenAPI 3.1 specification to avoid struct/function name collisions (ParseQueryResponse redeclared) when generating with oapi-codegen. These x-go-name hints are part of the documented fix path for the remaining 2 collision issues. Together with allOf wrapper schemas (for duplicate response type refs across GET/POST pairs), the real spec can generate a compilable 12K-line client. Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
Resolve all 9 ParseQueryResponse/ParseQueryPostResponse collisions in the real Prometheus OpenAPI 3.1 spec by: 1. Adding allOf wrapper schemas for 7 GET/POST response type pairs 2. Adding x-go-name extensions to /query operations 3. Renaming /parse-query operationId to /parse-promql to avoid struct name collision with /query parser functions The real spec now generates a compilable 12,757-line client. The wrapper (client.go) still uses our hand-crafted spec's type names since the real spec uses oneOf union types that need wrapper adaptation. Co-authored-by: atlarix-agent <agent@atlarix.dev> Signed-off-by: Amariah Kamau <110414493+AmariahAK@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #1998
@bwplotka @kakkoyun @vesari
i. What was the issue
The Prometheus HTTP API client at
api/prometheus/v1/api.gois entirely hand-written (1,585 lines). Maintaining it manually creates ongoing overhead — fixing bugs, syncing with Prometheus API changes, and maintaining customunsafe-based JSON decoders for performance. Prometheus now officially serves an OpenAPI 3.1/3.2 specification at/api/v1/openapi.yaml, opening the door to code generation. The question: can we replace or supplement the hand-written client with a generated one?ii. Where was the issue
The hand-written client uses
github.com/json-iterator/gowith customunsafedecoders for the hot-path types (SamplePair,SampleHistogramPair,SampleStream). A generated client would produce strict JSON-serializable Go structs using standardencoding/json, potentially losing both the convenientmodel.Valueinterface (Scalar/Vector/Matrix dispatch) and the optimized decoding performance.iii. How we investigated
Built a proof-of-concept under
exp/api/openapi/, following the existingexp/api/remote/pattern for experimental modules.Generated client:
spec.yaml, 1,055 lines) covering all 22 v1 API endpoints with proper response envelopes ({status, data, errorType, error, warnings, infos})openapi.gen.go, 5,216 lines) using oapi-codegen v2.8.0client.go) that dispatchesresultTypetomodel.Scalar/model.Vector/model.Matrixspec_real_31.yaml, 5,510 lines fromopenapi_3.1_golden.yaml), which generates 12,739 lines — compiles but producesParseQueryResponsename collisions from shared GET/POST response typesCompatibility verified:
RequestEditorFnfor bearer tokens and custom headersWithHTTPClientfor custom transportQueryOptionsstruct with Timeout, LookbackDelta, Stats, LimitPerformance (Apple M1):
The overhead comes from double JSON encoding:
json.Unmarshalinto generated types →json.Marshal→json.Unmarshalintomodel.Value. Custom oapi-codegen templates could inject json-iterator for the hot path.Tests:
iv. Why oapi-codegen over alternatives
v. How to test locally
Key findings for the maintainers
model.Valueretention requires a wrapper — theresultfield varies byresultType(array for scalar, array-of-objects for vector/matrix), which OpenAPI cannot express as a discriminated union. The wrapper round-trips through JSON to dispatch to the correct type.SamplePair/SampleHistogramPaircould close most of this gap.exp/api/openapi/follows the same pattern asexp/api/remote/and doesn't break the stable module.