Skip to content

feat(BA-4667): Add GraphQL API for prometheus query preset operations#9643

Open
seedspirit wants to merge 2 commits intomainfrom
BA-4667
Open

feat(BA-4667): Add GraphQL API for prometheus query preset operations#9643
seedspirit wants to merge 2 commits intomainfrom
BA-4667

Conversation

@seedspirit
Copy link
Contributor

Summary

  • Add Strawberry GraphQL API for prometheus query preset admin CRUD (create/modify/delete) and list/get queries with Node/Connection pagination pattern, restricted to SUPERADMIN
  • Add execute query (prometheusQueryPresetResult) available to all authenticated users, resolving presets by name and returning structured Prometheus results
  • Follow BEP-1050 spec with proper GQL types: PrometheusQueryPresetGQL, PrometheusPresetOptionsGQL, MetricLabelEntryGQL, MetricResultGQL, PrometheusQueryResultGQL, and corresponding input/filter/payload types

Test plan

  • Verify admin queries require SUPERADMIN access
  • Verify execute query works for all authenticated users
  • Verify Node/Connection pagination works correctly with cursor and offset modes
  • Verify filtering by name and metric_name works
  • Verify create/modify/delete mutations work correctly

Resolves BA-4667

Add Strawberry GraphQL types, resolvers, and fetchers for prometheus
query preset admin CRUD and execute query following BEP-1050 spec:

- Admin queries: adminPrometheusQueryPreset, adminPrometheusQueryPresets
  with Node/Connection pagination, filtering, and ordering
- Admin mutations: adminCreatePrometheusQueryPreset,
  adminModifyPrometheusQueryPreset, adminDeletePrometheusQueryPreset
- Execute query: prometheusQueryPresetResult (all authenticated users)
  resolves preset by name and returns PrometheusQueryResultGQL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 5, 2026 01:29
@github-actions github-actions bot added size:XL 500~ LoC comp:manager Related to Manager component labels Mar 5, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Strawberry GraphQL surface for Prometheus query preset CRUD/listing (admin-only) and preset execution (authenticated users), including Relay Node/Connection pagination and typed Prometheus result payloads.

Changes:

  • Introduces new GraphQL types (Node/Connection, inputs, filters/order-by, payloads) for Prometheus query presets and query results.
  • Adds query/mutation resolvers and fetchers, wiring them into the main GraphQL schema.
  • Extends repository query conditions to support created_at time filtering.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/ai/backend/manager/repositories/prometheus_query_preset/options.py Adds created_at before/after query conditions used by GQL filters.
src/ai/backend/manager/api/gql/schema.py Registers new Prometheus query preset queries/mutations in the root schema.
src/ai/backend/manager/api/gql/prometheus_query_preset/types/payloads.py Adds GraphQL result/payload types for Prometheus execution and delete payload.
src/ai/backend/manager/api/gql/prometheus_query_preset/types/node.py Adds Relay Node/Connection and create/modify payload types for presets.
src/ai/backend/manager/api/gql/prometheus_query_preset/types/inputs.py Adds inputs for CRUD and execution (labels, time range), mapping to service-layer specs.
src/ai/backend/manager/api/gql/prometheus_query_preset/types/filters.py Adds filter + order-by inputs for list queries, mapping to repository conditions/orders.
src/ai/backend/manager/api/gql/prometheus_query_preset/types/init.py Exports the new GQL types for package consumers.
src/ai/backend/manager/api/gql/prometheus_query_preset/resolver/query.py Implements admin get/list queries and the authenticated execution query by preset name.
src/ai/backend/manager/api/gql/prometheus_query_preset/resolver/mutation.py Implements admin create/modify/delete mutations.
src/ai/backend/manager/api/gql/prometheus_query_preset/resolver/init.py Re-exports resolvers for package wiring.
src/ai/backend/manager/api/gql/prometheus_query_preset/fetcher/preset.py Implements fetchers for list/get and execution, assembling Relay connections and result types.
src/ai/backend/manager/api/gql/prometheus_query_preset/fetcher/init.py Exports fetchers.
src/ai/backend/manager/api/gql/prometheus_query_preset/init.py Package exports for schema wiring and external imports.
changes/9643.feature.md Documents the new GraphQL API feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +96 to +108
if self.OR:
or_sub_conditions: list[QueryCondition] = []
for sub_filter in self.OR:
or_sub_conditions.extend(sub_filter.build_conditions())
if or_sub_conditions:
conditions.append(combine_conditions_or(or_sub_conditions))

if self.NOT:
not_sub_conditions: list[QueryCondition] = []
for sub_filter in self.NOT:
not_sub_conditions.extend(sub_filter.build_conditions())
if not_sub_conditions:
conditions.append(negate_conditions(not_sub_conditions))
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OR/NOT implementations flatten each sub-filter’s (implicitly AND-ed) conditions into a single list, which changes boolean semantics. Example: OR([{a,b},{c,d}]) becomes (a OR b OR c OR d) instead of (a AND b) OR (c AND d). Fix by first combining each sub_filter.build_conditions() into a single grouped condition (AND within the sub-filter), then OR those grouped conditions; similarly, NOT should negate grouped sub-filters (or apply De Morgan correctly), not a flattened list.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +86
class ModifyPrometheusQueryPresetInput:
name: str | None = strawberry.field(default=UNSET, description="New preset name.")
metric_name: str | None = strawberry.field(default=UNSET, description="New metric name.")
query_template: str | None = strawberry.field(default=UNSET, description="New PromQL template.")
time_window: str | None = strawberry.field(
default=UNSET, description="New default time window."
)
options: PrometheusPresetOptionsInput | None = strawberry.field(
default=UNSET, description="New preset options."
)

def to_updater(self, preset_id: UUID) -> Updater[PrometheusQueryPresetRow]:
spec = PrometheusQueryPresetUpdaterSpec()

if self.name is not UNSET and self.name is not None:
spec.name = OptionalState.update(self.name)

if self.metric_name is not UNSET and self.metric_name is not None:
spec.metric_name = OptionalState.update(self.metric_name)

if self.query_template is not UNSET and self.query_template is not None:
spec.query_template = OptionalState.update(self.query_template)
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modify input semantics for explicit null are inconsistent: time_window treats null as a deliberate nullify, but name/metric_name/query_template treat null as a no-op. For a TriState/OptionalState API this is surprising for clients. Consider either (a) making these fields str with default=UNSET (disallow null), or (b) supporting explicit nullification consistently (e.g., using TriState for these fields too).

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +40
def from_data(cls, data: PrometheusQueryPresetData) -> Self:
return cls(
id=ID(str(data.id)),
name=data.name,
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrometheusQueryPresetGQL.id is declared as NodeID[str], but from_data() assigns it using strawberry.ID. Even if both are string-like at runtime, mixing Relay NodeID and plain ID is easy to misread and can cause subtle issues with Relay/global-id expectations. Prefer assigning the internal NodeID value directly (e.g., a UUID string as the NodeID type) rather than wrapping it as ID.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:manager Related to Manager component size:XL 500~ LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants