Skip to content

Release 6.5.0

Latest

Choose a tag to compare

@github-actions github-actions released this 31 Aug 18:23

Release Notes — Cumulocity Dynamic Mapper v6.5.0

This release adds...

  • a new Google Cloud Pub/Sub connector — e.g. for ingestion into Google's Manufacturing Data Engine
  • an AI-generated alternative to code templates when creating Smart Functions,
  • switches the default transformation type for new mappings from JSONata to Smart Function,
  • bundles a connector's connection-lifecycle status transitions into a single event per connect/disconnect attempt instead of one independent event per status change,
  • fixes a bug that left devices permanently unreachable — with repeated error-log noise — once their auto-created device was deleted from inventory.
  • fixes two Message Explorer bugs — an "Edit session" drawer that could silently fail to close, and OUTBOUND sessions scoped to a device group never receiving any messages — an inventory-cache race that could intermittently drop outbound mappings using filterInventory, plus several follow-on fixes to AI Smart Function generation surfaced while working on the Message Explorer.

👤 Customer-Facing Changes

Changes that affect what users see or how the product behaves.

✨ New Features

  • New outbound connector: Google Cloud Pub/Sub #554
    • Publishes outbound Cumulocity data (Measurements, Alarms, Events, and any other API type) to a Google Cloud Pub/Sub topic — e.g. for ingestion into Google's Manufacturing Data Engine (MDE), which reads all data from a single Pub/Sub topic (input-messages by default) and dispatches it via custom parsers keyed off message attributes. Outbound-only; no inbound direction is supported.
    • Every published message carries the fixed attributes sourceSystem=cumulocity and messageType=<measurement|alarm|event|...> in addition to the mapping-defined JSON body, which is published unchanged as the message data.
    • Configurable Google Cloud project ID and target topic ID (a mapping's own publish topic, if set, overrides the connector default), publish acknowledgement timeout, and two authentication modes: a Service Account Key (JSON) or Application Default Credentials (ADC).
  • Google Cloud Pub/Sub connector: added inbound support #556
    • The connector is no longer outbound-only: it now also subscribes to an existing Pub/Sub subscription (configured via a new subscriptionId property) to receive inbound messages and dispatch them through the normal inbound mapping pipeline. The service account needs roles/pubsub.subscriber in addition to roles/pubsub.publisher.
    • Acknowledgement follows the mapping's QoS: at-most-once acks immediately before processing, while at-least-once (and higher) acks only after successful processing and nacks — triggering Pub/Sub redelivery — on a processing error, timeout, or interrupt.
    • Connection health now also reflects subscriber state: isPhysicallyConnected() returns false if any active subscriber has stopped running, not just on missing credentials.
  • Generate Smart Functions with AI instead of a code template #543
    • When creating a new Inbound/Outbound mapping with transformation type Smart Function (JavaScript), the "Select Payload Format and Transformation Type" step now offers an explicit choice — "Choose Code Template" vs. "Generate with AI" — whenever an AI agent is deployed for Smart Functions. The choice defaults to "Generate with AI" when an agent is available, falling back to the template dropdown otherwise.
    • The same "Generate with AI" vs. "Define Manually" choice is offered for transformation type JSONata (Substitution as JSONata Expression) whenever an AI agent is deployed for JSONata — generating the substitutions instead of a Smart Function code template.
    • Generation is deferred until the Transformation step, where the real source/target templates are available, and triggers automatically the first time that step is reached.
    • Fixed a related bug along the way: the auto-triggered generation could send a stale default source template to the AI agent instead of the user's just-edited one, because it relied on a change-event mirror that didn't fire for all edit paths (e.g. some tree-mode interactions). It now reads the live content directly from the JSON editor.
    • Fixed a bug that made AI generation appear to silently do nothing for OUTBOUND Smart Functions: outbound mappings have no fixed target template by design — the function body itself builds whatever payload the target broker/protocol expects — but generation treated the resulting empty targetTemplate as missing information and had the AI agent stop and ask a clarifying question instead of generating code, every single time. The prompt (and the agent's own system prompt) now explain that this is expected and instruct it to generate directly from the source template and topic instead. INBOUND generation was also missing a concrete reference for what its target Cumulocity payload should look like for a known targetAPI (e.g. MEASUREMENT) — a standard sample payload is now included.
    • Fixed "Generate with AI" not auto-opening on the Transformation step when creating a mapping from a captured message in the Message Explorer — that entry point never forwarded the user's "Generate with AI" choice into the stepper, so generation silently never started. Creating a mapping via the normal "+ Add mapping" flow was unaffected.
    • The AI prompt's very first chat message used to embed the mapping's full JSON (including managed-object metadata, self links, etc.) inline, making it a large, hard-to-read wall of raw JSON in the transcript. The mapping data is now sent to the agent as hidden grounding context instead, so the visible chat message is a short, readable sentence.
  • Open version history and see draft status directly from the Versions list #546
    • The Monitoring → Versions pages (Versions inbound / Versions outbound) now have a "Versions" row action that opens the existing version-history drawer for that mapping, instead of requiring a detour through the main Mapping grid.
    • Added a "Status" column showing a draft badge for any mapping with unpublished draft changes, so pending drafts are visible across the whole list at a glance instead of only after opening each mapping individually.
  • New mappings now default to Smart Function instead of JSONata #555
    • Creating a new mapping in non-Expert mode used to always preselect JSONata as the transformation type. It now preselects Smart Function (JavaScript) instead, matching updated user expectations. Expert Mode is unaffected — it still requires an explicit choice with no default.
  • Connector status log now shows one event per connect/disconnect attempt instead of one per status change #547
    • Every connector status transition (CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED, RETRYING, FAILED) used to be posted as an independent Cumulocity event, so a single connect attempt with one retry produced 3–4 unrelated rows in Monitoring → Service events and the per-connector Details & logs page, with no way to tell which rows belonged together. Each connection-lifecycle session is now bundled into a single event that's updated in place as it progresses — the same "History of changes" pattern Cumulocity Operations use — with an entry-count badge, an "open"/error indicator, and an expandable compact timeline of that session's transitions.
    • CONFIGURED (a settle/idle marker, never itself part of a connect/disconnect attempt) no longer generates its own event at all — it was showing up as an extra, low-signal bundle on every disconnect/reconnect cycle. The connector's live status display is unaffected; only the persisted event history changed.
    • Fixed a related bug found while verifying this in production: the MQTT and AMQP connectors could report the same physical disconnect twice — once via the explicit disconnect call, once via the underlying client library's own disconnected/exception listener firing as a side effect of that same call — which showed up as two separate sessions instead of one. Every other connector type (Kafka, Pulsar, HTTP, WebHook) was checked and found not to have this pattern.

🐛 Bug Fixes

  • Message Explorer: OUTBOUND sessions scoped to a device group never received any messages #551
    • Selecting a single device as the OUTBOUND source worked fine, but selecting a device group silently returned nothing. The service created a single Notification 2.0 subscription on the group's own managed object — which delivers no device events at all — and then filtered incoming messages by exact equality with the group's own id, which real device messages (carrying the device's id) could never match anyway.
    • Fixed by resolving the selected source into its concrete device set (a plain device resolves to itself, a group expands to every device it contains), subscribing each resolved device individually, and matching incoming messages by membership in that resolved set instead of exact-id equality. Session teardown now also unsubscribes the full expanded device set instead of just the original group id.
  • Message Explorer: "Edit session" drawer could silently fail to close #550
    • Editing an OUTBOUND session scoped to a device/group and clicking "Update" without reselecting a device did nothing — the drawer stayed open with no visible feedback. The edit drawer restored the session's topic, connector, and other fields from the existing session, but never restored the previously selected device into the asset selector, so the Update button stayed disabled the whole time (the click that "did nothing" was landing on a disabled button).
    • Fixed by restoring the selected device (and its cached name, to avoid a redundant inventory lookup) when the edit drawer opens.
  • Outbound filterInventory mapping intermittently dropped due to inventory-cache race #552
    • An outbound mapping using filterInventory (an inventory-based JSONata filter, e.g. type="some-type") was intermittently excluded from the candidate list for a matching INVENTORY notification, even when the filter expression was a constant that is always true for the device in question — observed as dropping anywhere from 1-in-6 to 4-of-5 notifications across repeated runs, with no correlation to any actual data change.
    • Root cause: InventoryCacheEnrichmentService.updateMOInInventoryCache() published an empty placeholder map into the shared inventory cache immediately, before the blocking REST call that actually populates it with fragments (including type). A concurrent filterInventory evaluation landing inside that window read the still-empty map, saw no type, and evaluated the filter to false — silently excluding the mapping from that message's candidates. Pure timing race, hence the non-deterministic reproduction.
    • Fixed by building the full map first and publishing it to the cache once, atomically, at the end — mirroring the pattern already used a few lines down in getMOFromInventoryCache() itself.
  • Deleted devices left the mapper permanently unable to re-create them #544
    • Once a device that the mapper had created implicitly (createNonExistingDevice: true) was later deleted from Cumulocity inventory, every subsequent inbound message for the same external ID kept resolving to the deleted device's stale internal id — forever. This produced a permanent per-message failure loop: repeated 404s from the notification subscription API, or 422 Source object does not exist in inventory from measurement/event creation, with the device never recreated even though createNonExistingDevice was enabled.
    • Root cause: TenantRegistry's external-ID resolution cache had no invalidation path — a removeFromExternalIdCache method existed for exactly this purpose but had never been wired up to any actual failure path. The mapper's Notification 2.0 stream also didn't help: it only reacted to UPDATE events, silently dropping DELETE (and CREATE) notifications without evicting anything.
    • Fixed by evicting the stale cache entry as soon as a deletion is discovered — via a real inventory lookup, via a Notification 2.0 DELETE event (now handled, with the now-pointless subscription torn down), and via the existing 422-retry path in the inbound message pipeline (which previously cleared a different, unrelated cache). A message for a deleted device's external ID still hits one failure to discover the deletion — unavoidable, that's how it's detected — but the very next message now gets a freshly created device instead of repeating the same failure indefinitely.
    • Also cleaned up related error-log noise: the same failure was previously logged three times, with a full stack trace each time, as it propagated up through nested handlers that just rethrew it unchanged. Now logged once, at the point where it's actually handled.

⚙️ Internal / Implementation Changes

Refactoring, code-quality, and platform maintenance work with no intended change in customer-visible behavior.

  • Added regression coverage for the device-recreation fix #544
    • New unit tests for TenantRegistry's external-ID cache reverse-index/eviction, for InventoryCacheEnrichmentService's device-not-found path, and for CacheInventoryUpdateClient's Notification 2.0 DELETE handling.
    • New shell integration test (test-inbound-implicit-device-recreate-after-delete.sh, registered in the inbound suite): create an implicit device, delete it, resend a message for the same external ID, and assert a new device is auto-created with a different internal id.

Cumulocity Dynamic Mapper · v6.5.0 · August 2026

All releases on GitHub

Full Changelog: v6.4.3...v6.5.0