Skip to content

[Synthetics] Create Internal Bulk PATCH API Endpoint for Monitors #268141

Draft
mgiota wants to merge 8 commits into
elastic:mainfrom
mgiota:synthetics-bulk-patch-api
Draft

[Synthetics] Create Internal Bulk PATCH API Endpoint for Monitors #268141
mgiota wants to merge 8 commits into
elastic:mainfrom
mgiota:synthetics-bulk-patch-api

Conversation

@mgiota
Copy link
Copy Markdown
Contributor

@mgiota mgiota commented May 7, 2026

Fixes #239579

In progress, description will be updated after every commit to include the new changes

Summary

  • Internal endpoint at PATCH /internal/synthetics/monitors/_bulk_update
  • Schema validation, route registration
  • The Synthetics route type system was extended to include PATCH so the new route can register correctly.
  • UpdateMonitorAPI preprocessing service: AAD-safe decrypt → merge → re-encrypt pipeline producing syncEditedMonitorBulk-compatible survivors with per-id errors (not_found, invalid_origin, validation_failed, forbidden)
  • Handler wires UpdateMonitorAPI into syncEditedMonitorBulk and returns 200 with { result: [{id, updated, error?}], errors? } in request id order; classifies failedConfigs (Fleet rollback) and per-SO bulkUpdate errors per id; 400 on empty attributes, 500 on sync exception
  • API integration tests: happy path (public + private locations, multi-id batch), AAD regression (partial patch round-trips through encrypt/decrypt without drift), per-id errors (mixed valid/missing, project-origin rejection, invalid schedule), input validation

Internal endpoint at PATCH /internal/synthetics/monitors/_bulk_update.
Lands the route surface only — schema validation, route registration,
and a 501 placeholder. Behaviour (decrypt -> merge -> re-encrypt ->
bulk write via syncEditedMonitorBulk) is wired in subsequent commits.

The Synthetics route type system was limited to GET/POST/PUT/DELETE;
extended to include PATCH so the new route can register correctly.
@github-actions github-actions Bot added the author:actionable-obs PRs authored by the actionable obs team label May 7, 2026
@infra-vault-gh-plugin-prod
Copy link
Copy Markdown

infra-vault-gh-plugin-prod Bot commented May 7, 2026

🤖 Jobs for this PR can be triggered through checkboxes. 🚧

ℹ️ To trigger the CI, please tick the checkbox below 👇

  • Click to trigger kibana-pull-request for this PR!
  • Click to trigger kibana-deploy-project-from-pr for this PR!
  • Click to trigger kibana-deploy-cloud-from-pr for this PR!
  • Click to trigger kibana-entity-store-performance-from-pr for this PR!
  • Click to trigger kibana-storybooks-from-pr for this PR!

@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I was planning to use edit_monitor_bulk.ts, but there is already such a file. Current edit_monitor_bulk.ts is just a helper file and no actual route implementation. I left it as is and used update_monitor_bulk instead.

@mgiota mgiota changed the title [Synthetics] Add PATCH /monitors/_bulk_update route skeleton (1/5) [Synthetics] Create Internal Bulk PATCH API Endpoint for Monitors May 7, 2026
AAD-safe per-id pipeline for the bulk PATCH endpoint, mirroring the
DeleteMonitorAPI / ResetMonitorAPI pattern:
  decrypt (one SO round-trip via CONFIG_ID filter)
  → not_found diff
  → reject non-'ui' origins
  → mergeSourceMonitor (keeps prev AAD attrs the patch didn't touch)
  → validateMonitor (io-ts)
  → permissions + multi-space + private-location-spaces
  → bump revision, reset CONFIG_HASH, formatSecrets
Output is the MonitorConfigUpdate shape syncEditedMonitorBulk expects.
Route handler still returns 501; wiring comes next.
Tests cover each per-id error path, mixed batches, the conditional
private-locations fetch, and an AAD-preservation regression check.
@mgiota mgiota self-assigned this May 7, 2026
mgiota added 2 commits May 7, 2026 14:09
The handler returns 200 with `{ result, errors? }` where each entry of
`result` reports `{ id, updated, error? }` per requested id, in input
order. Entries are produced by:

  UpdateMonitorAPI.execute → { survivors, perIdErrors }
  → fetch private locations for the union of request space and every
    survivor's KIBANA_SPACES
  → syncEditedMonitorBulk
  → merge perIdErrors, failedConfigs (Fleet-rolled-back), per-SO
    bulkUpdate errors, and successful editedMonitors into one result
    array; publicSyncErrors become top-level `errors`

Edge cases:
  - empty `attributes`           → 400
  - no survivors after preprocess → 200 with all-errors result, sync
                                    skipped
  - syncEditedMonitorBulk throws  → 500 (orchestrator already rolled
                                    back)
  - happy path: public location, private location, multi-id batch
  - AAD regression: patch `enabled`, re-fetch via GET (decrypt path),
    assert secrets and other AAD fields round-trip unchanged. If
    mergeSourceMonitor ever drops prev AAD attrs, GET turns into 500.
  - per-id errors: mixed valid/missing ids, project-origin rejection,
    invalid-schedule validation failure
  - input validation: empty attributes → 400, empty ids → 400
@mgiota mgiota added backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes labels May 7, 2026
@kibanamachine
Copy link
Copy Markdown
Contributor

kibanamachine commented May 7, 2026

💔 Build Failed

Failed CI Steps

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
synthetics 1.1MB 1.1MB +79.0B

History

cc @mgiota

kibanamachine and others added 4 commits May 7, 2026 12:26
Switch the bulk update endpoint from an internal PATCH route to a public
PUT /api/synthetics/monitors/_bulk_update, matching the single-monitor
partial-update convention (PUT) and the existing public _bulk_delete route.
Drops PATCH from the supported route methods and updates unit + API tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
The bulk update preprocessing loop resolved the Elastic-managed-locations
capability and the saved-objects bulk_update privilege once per monitor.
Both answers are request-scoped, so cache the capability for the whole
request and the privilege per unique space set, turning O(N) permission
round-trips into O(1) / O(distinct space sets).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author:actionable-obs PRs authored by the actionable obs team backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Synthetics] Create Internal Bulk PATCH API Endpoint for Monitors

3 participants