Skip to content

NMS-19875: Topology assets: image storage for view backgrounds and custom node icons#8563

Open
marshallmassengill wants to merge 7 commits into
release-36.xfrom
NMS-19875-topology-assets-smoke
Open

NMS-19875: Topology assets: image storage for view backgrounds and custom node icons#8563
marshallmassengill wants to merge 7 commits into
release-36.xfrom
NMS-19875-topology-assets-smoke

Conversation

@marshallmassengill

@marshallmassengill marshallmassengill commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Custom topology views need server-side image storage for two planned
features: background images (floor plans, rack diagrams) behind the canvas,
and operator-uploaded node icons. Both reduce to the same problem — binary
image bytes referenced by id from a view's definition document — so one
shared asset catalog serves both, distinguished by a kind
(background | icon).

  • New features/topology-assets module: image bytes in the generic binary
    key-value store (PostgresBlobStore, kvstore_bytea) and catalog metadata
    (name, kind, MIME type, size, owner, timestamps) as a JSON document in the
    JSON key-value store, keyed by the same generated UUID — listings never
    drag image payloads along. No new schema. The blob store is constructed
    internally from the DataSource: the only BlobStore exposed as a shared
    service in the core context is thresholding's no-op, and registering a
    second one would make that lookup ambiguous.
  • /api/v2/topology/assets (TopologyAssetRestService): POST raw image
    bytes (no multipart; name/kind as query parameters), GET list with
    optional ?kind= filter, GET {id} serving the bytes under the asset's
    own content type with an ETag + max-age (icons reused across many nodes
    revalidate as 304s), GET {id}/meta, DELETE. Raster types only
    (png/jpeg/gif/webp); SVG is excluded for now since it can carry active
    content and assets are served from the application origin. Per-kind size
    caps: icons 512 KiB, backgrounds 10 MiB.
  • Access is the standard /api/v2 RBAC: any authenticated user reads,
    ROLE_REST/ROLE_ADMIN writes.
  • The base assembly now ships the module and the Postgres BlobStore
    implementation (only the no-op blob store was in lib before).

Testing: TopologyAssetDaoIT (3) against a temporary database and
TopologyAssetRestServiceIT (3) covering the upload/serve/delete lifecycle,
byte integrity, ETag revalidation, kind filtering, and the validation
statuses (400/413/415/404). Also verified live (upload → byte-identical
serve → 304 → delete; anonymous → 401).

The consuming UI (background rendering and the node icon picker) ships in
the topology UI PR; this PR is the storage/API half and is independently
mergeable (the UI degrades gracefully without it).

Assisted-by: Claude Code:Opus 4.8/Fable 5

Jira: https://opennms.atlassian.net/browse/NMS-19875

Custom topology views need server-side image storage for two upcoming
features: background images (floor plans, rack diagrams) behind the
canvas, and operator-uploaded node icons. Both are the same problem --
binary image bytes referenced by id from a view's definition document --
so one shared asset catalog serves both, distinguished by a 'kind'.

The new features/topology-assets module stores the bytes in the generic
binary key-value store (PostgresBlobStore, kvstore_bytea) and the
catalog metadata as a JSON document in the JSON key-value store, keyed
by the same generated UUID, so listings never drag image payloads
along. The blob store is constructed internally from the DataSource
rather than injected: the only BlobStore exposed as a shared service in
the core context is thresholding's no-op, and registering a second one
would make that lookup ambiguous.
Upload, serve, list, and delete topology image assets. Uploads are the
raw image bytes under their own content type (no multipart), with the
name and kind as query parameters; raster types only (SVG is excluded
for now -- it can carry active content and these are served from the
application origin), and each kind has its own size cap: icons 512 KiB,
backgrounds 10 MiB. Bytes are served with an ETag derived from the
asset's last-modified time plus a max-age, so icons reused across many
nodes revalidate as 304s. Access is the standard /api/v2 RBAC: any
authenticated user reads, ROLE_REST/ROLE_ADMIN writes.
Add the topology-assets jar and the Postgres BlobStore implementation
to the base assembly -- only the no-op blob store shipped in lib until
now.
TopologyAssetDaoIT runs the DAO against a real temporary database --
metadata through the PostgresJsonStore and bytes through a
PostgresBlobStore, the same pairing the production wiring builds.
TopologyAssetRestServiceIT drives /api/v2/topology/assets through the
CXF harness: the upload/serve/delete lifecycle, byte integrity, ETag
revalidation (304), kind filtering, and the validation statuses (400
missing name/kind, 413 over the per-kind cap, 415 non-image type, 404
unknown id).
…ts-rest-r36

Adapts the topology-assets module's parent reference to the
36.0.2-SNAPSHOT version bump that came with the 36.0.1 release.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds server-side storage and an /api/v2 REST API for topology view image assets (backgrounds and custom node icons), storing metadata and bytes separately using the existing Postgres-backed KV stores.

Changes:

  • Introduces a new features/topology-assets module providing the TopologyAsset model and a KV-store-backed TopologyAssetDao implementation.
  • Adds TopologyAssetRestService (/api/v2/topology/assets) to upload/list/fetch/delete assets, including caching via ETag and per-kind size limits.
  • Wires the new feature into the build/assembly and adds integration tests for both DAO and REST lifecycle/validation behavior.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
opennms-webapp-rest/src/test/java/org/opennms/web/rest/v2/TopologyAssetRestServiceIT.java CXF-based integration tests for upload/serve/list/filter/delete + validation status codes.
opennms-webapp-rest/src/main/java/org/opennms/web/rest/v2/TopologyAssetRestService.java New JAX-RS v2 resource implementing the topology assets REST API.
opennms-webapp-rest/src/main/java/org/opennms/web/rest/v2/TopologyAssetDTO.java DTO representing asset metadata returned by the REST API.
opennms-webapp-rest/pom.xml Adds dependency on the new org.opennms.features.topology-assets module.
opennms-base-assembly/pom.xml Ships the topology-assets module and Postgres BlobStore implementation in the base assembly.
features/topology-assets/src/test/java/org/opennms/netmgt/topology/assets/TopologyAssetDaoIT.java DAO integration tests against a temporary database (metadata + bytes).
features/topology-assets/src/main/resources/META-INF/opennms/component-dao.xml Spring/OSGi wiring exposing TopologyAssetDao.
features/topology-assets/src/main/java/org/opennms/netmgt/topology/assets/TopologyAssetDao.java DAO API for storing/fetching/listing/deleting assets and bytes.
features/topology-assets/src/main/java/org/opennms/netmgt/topology/assets/TopologyAsset.java Asset metadata model (id, kind, mimeType, size, owner, timestamps).
features/topology-assets/src/main/java/org/opennms/netmgt/topology/assets/impl/TopologyAssetKvStore.java KV-store-backed DAO implementation using JsonStore + PostgresBlobStore.
features/topology-assets/pom.xml New feature module POM and dependencies.
features/pom.xml Registers the new topology-assets module in the features build.

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

@marshallmassengill marshallmassengill changed the base branch from develop to release-36.x June 11, 2026 18:20
- Make null-handling in TopologyAssetKvStore.get explicit
  (flatMap/ofNullable) so a malformed stored document reads as absent.
  (Optional.map already wrapped null results, so this is clarity, not a
  behavior change.)
- Answer 415 with a clear message when an upload carries no
  Content-Type header at all, instead of an NPE-driven 500.
- IT: the query-string helper now presents decoded parameter values
  (as a servlet container would) while the query string stays encoded.
- IT: assert the empty catalog by parsed JSON size rather than the raw
  response string.
…context

The BSM and status REST ITs load the shared /api/v2 CXF context, which
instantiates every v2 resource -- including the new topology-assets
service. Its model jar and the jsonStore bean's module are declared
provided in opennms-webapp-rest (the jars ship in $OPENNMS_HOME/lib),
and provided dependencies are not transitive, so those modules' test
classpaths lacked them: NoClassDefFoundError on TopologyAsset took down
all 56 tests. Declare both as test-scope dependencies where the context
is booted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants