From 966309f7852c23bbcfa9a61b04fa4cfc66cc2472 Mon Sep 17 00:00:00 2001
From: Mario Celi <mcelicalderon@gitlab.com>
Date: Tue, 2 Aug 2022 12:07:48 -0500
Subject: [PATCH] Add `issuableDatesUpdated` subscription to GraphQL API

Changelog: added

Updating start or due date in an Issue or WorkItem will now
trigger a relatime update so you can fetch the updated dates
---
 app/graphql/graphql_triggers.rb               |  4 ++
 app/graphql/types/subscription_type.rb        |  3 ++
 app/services/issues/update_service.rb         |  7 +++
 spec/graphql/graphql_triggers_spec.rb         | 14 ++++++
 spec/graphql/types/subscription_type_spec.rb  |  1 +
 spec/services/issues/update_service_spec.rb   | 46 +++++++++++++++++++
 .../work_items/update_service_spec.rb         | 34 ++++++++++++++
 7 files changed, 109 insertions(+)

diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index 342cff83e907d..b39875b83a99e 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -16,4 +16,8 @@ def self.issuable_title_updated(issuable)
   def self.issuable_labels_updated(issuable)
     GitlabSchema.subscriptions.trigger('issuableLabelsUpdated', { issuable_id: issuable.to_gid }, issuable)
   end
+
+  def self.issuable_dates_updated(issuable)
+    GitlabSchema.subscriptions.trigger('issuableDatesUpdated', { issuable_id: issuable.to_gid }, issuable)
+  end
 end
diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb
index 18d6d51a30cd8..9b5f028a8570a 100644
--- a/app/graphql/types/subscription_type.rb
+++ b/app/graphql/types/subscription_type.rb
@@ -15,5 +15,8 @@ class SubscriptionType < ::Types::BaseObject
 
     field :issuable_labels_updated, subscription: Subscriptions::IssuableUpdated, null: true,
                                     description: 'Triggered when the labels of an issuable are updated.'
+
+    field :issuable_dates_updated, subscription: Subscriptions::IssuableUpdated, null: true,
+                                   description: 'Triggered when the due date or start date of an issuable is updated.'
   end
 end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index afc61eed2872e..46c28d82ddc49 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -70,6 +70,7 @@ def handle_changes(issue, options)
       handle_severity_change(issue, old_severity)
       handle_escalation_status_change(issue)
       handle_issue_type_change(issue)
+      handle_date_changes(issue)
     end
 
     def handle_assignee_changes(issue, old_assignees)
@@ -116,6 +117,12 @@ def move_issue_to_new_project(issue)
 
     attr_reader :spam_params
 
+    def handle_date_changes(issue)
+      return unless issue.previous_changes.slice('due_date', 'start_date').any?
+
+      GraphqlTriggers.issuable_dates_updated(issue)
+    end
+
     def clone_issue(issue)
       target_project = params.delete(:target_clone_project)
       with_notes = params.delete(:clone_with_notes)
diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb
index 84af33a5cb3f0..5e2ab74a0e5c6 100644
--- a/spec/graphql/graphql_triggers_spec.rb
+++ b/spec/graphql/graphql_triggers_spec.rb
@@ -47,4 +47,18 @@
       GraphqlTriggers.issuable_labels_updated(issue)
     end
   end
+
+  describe '.issuable_dates_updated' do
+    it 'triggers the issuableDatesUpdated subscription' do
+      work_item = create(:work_item)
+
+      expect(GitlabSchema.subscriptions).to receive(:trigger).with(
+        'issuableDatesUpdated',
+        { issuable_id: work_item.to_gid },
+        work_item
+      ).and_call_original
+
+      GraphqlTriggers.issuable_dates_updated(work_item)
+    end
+  end
 end
diff --git a/spec/graphql/types/subscription_type_spec.rb b/spec/graphql/types/subscription_type_spec.rb
index 1a2629ed422a1..9b043fa52cf0c 100644
--- a/spec/graphql/types/subscription_type_spec.rb
+++ b/spec/graphql/types/subscription_type_spec.rb
@@ -9,6 +9,7 @@
       issue_crm_contacts_updated
       issuable_title_updated
       issuable_labels_updated
+      issuable_dates_updated
     ]
 
     expect(described_class).to have_graphql_fields(*expected_fields).only
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index e2e8828ae89d1..9ef969d802b44 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -988,6 +988,52 @@ def update_issue(opts)
       end
     end
 
+    context 'updating dates' do
+      subject(:result) { described_class.new(project: project, current_user: user, params: params).execute(issue) }
+
+      let(:updated_date) { 1.week.from_now.to_date }
+
+      shared_examples 'issue update service that triggers date updates' do
+        it 'triggers graphql date updated subscription' do
+          expect(GraphqlTriggers).to receive(:issuable_dates_updated).with(issue).and_call_original
+
+          result
+        end
+      end
+
+      shared_examples 'issue update service that does not trigger date updates' do
+        it 'does not trigger date updated subscriptions' do
+          expect(GraphqlTriggers).not_to receive(:issuable_dates_updated)
+
+          result
+        end
+      end
+
+      context 'when due_date is updated' do
+        let(:params) { { due_date: updated_date } }
+
+        it_behaves_like 'issue update service that triggers date updates'
+      end
+
+      context 'when start_date is updated' do
+        let(:params) { { start_date: updated_date } }
+
+        it_behaves_like 'issue update service that triggers date updates'
+      end
+
+      context 'when no date is updated' do
+        let(:params) { { title: 'should not trigger date updates' } }
+
+        it_behaves_like 'issue update service that does not trigger date updates'
+      end
+
+      context 'when update is not successful but date is provided' do
+        let(:params) { { title: '', due_date: updated_date } }
+
+        it_behaves_like 'issue update service that does not trigger date updates'
+      end
+    end
+
     context 'updating asssignee_id' do
       it 'does not update assignee when assignee_id is invalid' do
         update_issue(assignee_ids: [-1])
diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb
index dd5924e743459..7715fad970337 100644
--- a/spec/services/work_items/update_service_spec.rb
+++ b/spec/services/work_items/update_service_spec.rb
@@ -36,6 +36,14 @@
       stub_spam_services
     end
 
+    shared_examples 'update service that triggers graphql dates updated subscription' do
+      it 'triggers graphql subscription issueableDatesUpdated' do
+        expect(GraphqlTriggers).to receive(:issuable_dates_updated).with(work_item).and_call_original
+
+        update_work_item
+      end
+    end
+
     context 'when title is changed' do
       let(:opts) { { title: 'changed' } }
 
@@ -187,6 +195,32 @@
         end
       end
 
+      context 'for start and due date widget' do
+        let(:updated_date) { 1.week.from_now.to_date }
+
+        context 'when due_date is updated' do
+          let(:widget_params) { { start_and_due_date_widget: { due_date: updated_date } } }
+
+          it_behaves_like 'update service that triggers graphql dates updated subscription'
+        end
+
+        context 'when start_date is updated' do
+          let(:widget_params) { { start_and_due_date_widget: { start_date: updated_date } } }
+
+          it_behaves_like 'update service that triggers graphql dates updated subscription'
+        end
+
+        context 'when no date param is updated' do
+          let(:opts) { { title: 'should not trigger' } }
+
+          it 'does not trigger date updated subscription' do
+            expect(GraphqlTriggers).not_to receive(:issuable_dates_updated)
+
+            update_work_item
+          end
+        end
+      end
+
       context 'for the hierarchy widget' do
         let(:opts) { { title: 'changed' } }
         let_it_be(:child_work_item) { create(:work_item, :task, project: project) }
-- 
GitLab