Fetch work item CRM contacts in their own query

What does this MR do and why?

This MR splits the loading of crmContacts widget from main work items query (i.e. namespaceWorkItem) into its own query that loads separate from base query, allowing us to bring down the query complexity of base query.

With this change in place, we're also able to remove 8 query overrides from ee/spec/graphql/all_queries_spec.rb. 🎉

Implementation notes

Here's a high level summary of what this MR does:

  • We've split the crmContacts widget from base work item query to its own query.
  • Unlike the other two splits, base query keeps crmContacts { contactsAvailable } rather than just type, because the sidebar gates this widget on contactsAvailable (i.e. whether CRM is enabled for the group) and not merely on whether the widget is supported, see workItemCrmContacts computed in work_item_attributes_wrapper.vue. Only the expensive contacts { nodes { ... } } selection moves out, same for the widgets path when flag is off.
  • The work_items/components/work_item_crm_contacts.vue component now owns its query, and edits go through a dedicated workItemUpdateCrmContacts mutation that returns the same fragment, so selecting contacts from the dropdown keeps updating the exact cache slot the query reads.
  • A failed query now reports through the page-level alert alongside the Sentry capture, instead of failing silently and leaving the widget looking like the item simply has no contacts.
  • There's no subscription here, unlike !252573 (merged) and !252578 (merged). Issues::SetCrmContactsService triggers issue_crm_contacts_updated rather than work_item_updated, so /add_contacts and /remove_contacts have never updated this widget in real time, on master either. Splitting the query doesn't change that, and pointing a subscription at workItemUpdated wouldn't help since that topic never fires for a contacts change. Worth a follow-up, but out of scope here.
  • Creating a work item writes contacts into the local draft cache, so graphql/resolvers.js now targets the new document for that draft update.
  • With this last split in place we can drop 8 per-file overrides from ee/spec/graphql/all_queries_spec.rb: the detail queries, mutations and the subscription all fit under the default limit now. The list queries keep theirs, because the analyzer scores both the widgets and features branches even though only one is ever requested.
  • This is the last of the three splits, so with all of them merged, work items load anonymously with the FF enabled globally. 🎉 Locally the namespaceWorkItem document now scores 191 against the 200 anonymous cap.

References

Screenshots or screen recordings

Note: There's no behaviour change when work_item_features_field feature flag is off, as Work Items continue to remain accessible for both logged-in and anonymous users. The recordings below indicate the behaviour for when flag is turned on.

Before After
Logged In No change, work items remained accessible before too while logged in
Anonymous

How to set up and validate locally

  1. Enable the flag with Feature.enable(:work_item_features_field).
  2. Open a work item in a project whose group has CRM contacts, and confirm the Contacts widget renders.
  3. Add and remove a contact, and confirm in the network tab that only workItemUpdateCrmContacts fires, with no namespaceWorkItem or workItemCrmContacts refetch.
  4. Reload and confirm the change persisted.
  5. Repeat with the flag disabled to check the widgets path.
  6. Start a new work item, add a contact to the draft, and confirm the draft survives a reload.
  7. With the flag still enabled, sign out and open a public work item, and confirm it renders instead of Work Item not found.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Kushal Pandya

Merge request reports

Loading
Loading