Skip to content

[Cases] Fix unified attachment gaps when attachments.enabled is true#257767

Closed
patrykkopycinski wants to merge 2 commits into
elastic:mainfrom
patrykkopycinski:fix/cases-unified-attachment-gaps
Closed

[Cases] Fix unified attachment gaps when attachments.enabled is true#257767
patrykkopycinski wants to merge 2 commits into
elastic:mainfrom
patrykkopycinski:fix/cases-unified-attachment-gaps

Conversation

@patrykkopycinski
Copy link
Copy Markdown
Contributor

@patrykkopycinski patrykkopycinski commented Mar 13, 2026

Summary

When xpack.cases.attachments.enabled is true, unified value attachments (stored as cases-attachments SO type) are created correctly but cannot be displayed, fetched, or deleted through the existing Cases plugin code paths. This is because multiple server-side methods and client-side utilities still hardcode cases-comments as the only SO type.

This PR fixes all identified gaps to provide end-to-end support for unified value attachments.


Gaps Found & Fixes Applied

1. AttachmentService.find() — only queries cases-comments

File: server/services/attachments/index.ts
Gap: The find() method hardcodes type: CASE_COMMENT_SAVED_OBJECT when querying the SO client, so unified attachments stored as cases-attachments are invisible.
Fix: Dynamically determine types based on getAttachmentSavedObjectType(config). When unified is enabled, queries both [CASE_ATTACHMENT_SAVED_OBJECT, CASE_COMMENT_SAVED_OBJECT]. Decodes each result using the appropriate schema (UnifiedAttachmentAttributesRt for unified, AttachmentAttributesRtV2 for legacy).

2. AttachmentGetter.get() — only queries cases-comments

File: server/services/attachments/operations/get.ts
Gap: The single-attachment get() method hardcodes CASE_COMMENT_SAVED_OBJECT, causing 404s when trying to get a unified attachment (e.g., during delete).
Fix: When unified is enabled, try CASE_ATTACHMENT_SAVED_OBJECT first; fall back to CASE_COMMENT_SAVED_OBJECT. Decode with the appropriate schema based on so.type.

3. AttachmentGetter.bulkGet() — only queries cases-comments

File: server/services/attachments/operations/get.ts
Gap: The bulkGet() method (used by user actions timeline to populate latestAttachments) only queries CASE_COMMENT_SAVED_OBJECT. User action references may point to attachments that exist in cases-attachments.
Fix: When unified is enabled, issue two parallel bulkGet calls (one for each SO type) and merge results, preferring the new type when found. transformAndDecodeBulkGetResponse now handles CASE_ATTACHMENT_SAVED_OBJECT types by decoding with UnifiedAttachmentAttributesRt.

4. client/attachments/bulk_get.ts — authorization assumes owner field

File: server/client/attachments/bulk_get.ts
Gap: Authorization logic calls getAndEnsureAuthorizedEntities on all attachments, but unified attachments don't have an owner field. Also, flattenCommentSavedObjects doesn't handle unified SO shape.
Fix: Partition fetched attachments into unifiedAttachments and legacyAttachments. Only apply authorization to legacy. Flatten unified attachments separately (direct attribute spread). Combine both lists.

5. BulkGetAttachmentsResponseRt — v1-only decoder

File: common/types/api/attachment/v1.ts
Gap: The BulkGetAttachmentsResponseRt.attachments field uses AttachmentsRt (v1-only), which fails to decode unified attachments.
Fix: Changed to rt.array(AttachmentRtV2) which accepts both v1 and unified attachment shapes.

6. UserActionInternalFindResponse.latestAttachments — v1-only type

File: common/types/api/user_action/v1.ts
Gap: The latestAttachments interface field was typed as Attachments (v1-only), causing type mismatch with the now-widened bulk get response.
Fix: Changed to BulkGetAttachmentsResponse['attachments'].

7. convertAttachmentsToCamelCase — v1-only input type

File: public/api/utils.ts
Gap: Parameter accepts only Attachment[], but the server now returns a mix of v1 and unified attachments.
Fix: Widened to Array<Attachment | AttachmentRequestV2>.

8. Case resolve — v1 decoder fails on unified attachments

File: server/client/cases/get.ts
Gap: The resolve() function passes all comments (including unified) to CaseResolveResponseRt, which is a v1-only decoder. Unified attachments lack comment, owner, etc. fields, causing decode errors.
Fix: Filter out unified attachments from the comments array before passing to the v1 decoder. Unified attachments are rendered separately via the unifiedAttachmentTypeRegistry in the UI.

9. Case encode — v1 decoder fails on unified attachments

File: server/common/models/case_with_comments.ts
Gap: Same issue as resolve — encodeWithComments() passes all SOs to flattenCommentSavedObjectsCaseRt, which chokes on unified shapes.
Fix: Filter to CASE_COMMENT_SAVED_OBJECT only before flattening.

10. getAll — authorization assumes owner field

File: server/client/attachments/get.ts
Gap: ensureSavedObjectsAreAuthorized maps over all SOs assuming .attributes.owner exists. Also, flattenCommentSavedObjectsAttachmentsRt fails on unified shapes.
Fix: Filter to legacy comments only for authorization and flattening.

11. Delete single attachment — get() + owner + decoder failures

File: server/client/attachments/delete.ts
Gap: deleteComment() calls get() (gap #2), then accesses .attributes.owner (undefined for unified), runs AttachmentRequestRt decoder (fails for unified), and creates a user action with owner (undefined).
Fix: Check attachment.type === CASE_ATTACHMENT_SAVED_OBJECT to detect unified. Skip authorization, v1 decode, user action creation, and alert handling for unified attachments (they have no owner, no alert fields).

12. Delete all attachments — owner access on unified SOs

File: server/client/attachments/delete.ts
Gap: deleteAll() maps over all SOs to get comment.attributes.owner for authorization and user action creation. Crashes on unified attachments.
Fix: Partition into legacy vs unified. Only run authorization/user-action/alert logic on legacy. bulkDelete already handles both types.

13. CaseCommentModel — user action methods assume legacy shape

File: server/common/models/case_with_comments.ts
Gap: createCommentUserAction assumes AttachmentRequest type (legacy). Unified attachments use UnifiedAttachmentPayload and need a separate user action path.
Fix: Split into createLegacyCommentUserAction and createUnifiedCommentUserAction. Route based on isLegacyAttachmentRequest() check. Also pass owner through to create/bulkCreate service calls.

14. UI — unified attachment rendering in user actions timeline

Files: public/components/user_actions/comment/comment.tsx, unified_value.tsx, registered_attachments.tsx, types.ts, mock.ts, user_actions_list.tsx
Gap: The comment builder switch statement doesn't handle unified attachment types. The UserActionBuilderArgs doesn't include unifiedAttachmentTypeRegistry.
Fix: Added unifiedAttachmentTypeRegistry to builder args. In the default case, check if the attachment type is registered in the unified registry and delegate to createUnifiedValueAttachmentUserActionBuilder. Created unified_value.tsx to bridge unified attachments into the existing createRegisteredAttachmentUserActionBuilder. Widened the generic constraint on createRegisteredAttachmentUserActionBuilder to accept Attachment | UnifiedAttachment.


Test plan

  • Enable xpack.cases.attachments.enabled: true in kibana.dev.yml
  • Create a case and add a unified value attachment (e.g., via Agent Builder mermaid tool)
  • Verify the attachment appears in the case Activity tab
  • Verify the "Delete attachment" action works without errors
  • Verify the case details page loads without console errors
  • Add a legacy comment (text) to the same case — verify it coexists with unified attachments
  • Run existing Cases Jest tests: yarn test:jest x-pack/platform/plugins/shared/cases

@elasticmachine
Copy link
Copy Markdown
Contributor

🤖 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!

@patrykkopycinski
Copy link
Copy Markdown
Contributor Author

/ci

…bled` is true

When the unified attachments feature flag (`xpack.cases.attachments.enabled`)
is enabled, several code paths in the Cases plugin still hardcode the legacy
`cases-comments` Saved Object type, causing unified attachments stored in
`cases-attachments` to be invisible, unreadable, or undeletable.

This PR fixes all identified gaps to ensure end-to-end support for unified
value attachments (find, get, bulkGet, delete, resolve, encode, and UI
rendering).
@patrykkopycinski patrykkopycinski force-pushed the fix/cases-unified-attachment-gaps branch from 0546284 to 6caaa6f Compare March 13, 2026 21:27
@patrykkopycinski
Copy link
Copy Markdown
Contributor Author

/ci

@elasticmachine
Copy link
Copy Markdown
Contributor

elasticmachine commented Mar 13, 2026

💔 Build Failed

Failed CI Steps

Test Failures

  • [job] [logs] Jest Tests #2 / AttachmentService feature flag (config.attachments.enabled) when enabled, bulkCreate writes to CASE_ATTACHMENT_SAVED_OBJECT
  • [job] [logs] Jest Tests #2 / AttachmentService feature flag (config.attachments.enabled) when enabled, bulkCreate writes to CASE_ATTACHMENT_SAVED_OBJECT
  • [job] [logs] Jest Tests #2 / AttachmentService feature flag (config.attachments.enabled) when enabled, create writes to CASE_ATTACHMENT_SAVED_OBJECT with unified attributes
  • [job] [logs] Jest Tests #2 / AttachmentService feature flag (config.attachments.enabled) when enabled, create writes to CASE_ATTACHMENT_SAVED_OBJECT with unified attributes
  • [job] [logs] Jest Tests #8 / CaseCommentModel create does not remove alerts not attached to the case
  • [job] [logs] Jest Tests #8 / CaseCommentModel create does not remove alerts not attached to the case
  • [job] [logs] Jest Tests #8 / CaseCommentModel create does not remove comments when filtering out duplicate alerts
  • [job] [logs] Jest Tests #8 / CaseCommentModel create does not remove comments when filtering out duplicate alerts
  • [job] [logs] Jest Tests #8 / CaseCommentModel create remove alerts attached to the case
  • [job] [logs] Jest Tests #8 / CaseCommentModel create remove alerts attached to the case
  • [job] [logs] Jest Tests #8 / CaseCommentModel create remove multiple alerts
  • [job] [logs] Jest Tests #8 / CaseCommentModel create remove multiple alerts
  • [job] [logs] FTR Configs #144 / cases security and spaces enabled: basic Common bulk_create_attachments v2 unified attachment creates a comment attachmentwith v2 payload (no owner) and resolves owner from case
  • [job] [logs] FTR Configs #144 / cases security and spaces enabled: basic Common bulk_create_attachments v2 unified attachment creates a comment attachmentwith v2 payload (no owner) and resolves owner from case
  • [job] [logs] FTR Configs #147 / cases security and spaces enabled: trial Common bulk_create_attachments v2 unified attachment creates a comment attachmentwith v2 payload (no owner) and resolves owner from case
  • [job] [logs] FTR Configs #147 / cases security and spaces enabled: trial Common bulk_create_attachments v2 unified attachment creates a comment attachmentwith v2 payload (no owner) and resolves owner from case

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
cases 2073 2074 +1

Async chunks

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

id before after diff
cases 2.0MB 2.0MB +1010.0B

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
cases 202.4KB 202.4KB +21.0B

History

cc @patrykkopycinski

@christineweng
Copy link
Copy Markdown
Contributor

Hey @patrykkopycinski, thanks for putting this together, just FYI, I just opened #256133 that addressed these gaps. it is an intended part 2 for the attachment V2 work

@patrykkopycinski
Copy link
Copy Markdown
Contributor Author

Thank you @christineweng, looking forward for V2 attachments 💪

@christineweng
Copy link
Copy Markdown
Contributor

Hey @patrykkopycinski! #256133 is merged so these gaps are filled. just FYI the feature flag for V2 is set to enabled for 9.5

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.

4 participants