From f27e972e84b488fb96202772872379113f72c789 Mon Sep 17 00:00:00 2001
From: Robert Schilling <rschilling@student.tugraz.at>
Date: Wed, 9 Nov 2016 15:14:02 +0100
Subject: [PATCH] Grapify milestones API

---
 lib/api/milestones.rb                | 111 ++++++++++++++-------------
 spec/requests/api/milestones_spec.rb |   9 +++
 2 files changed, 66 insertions(+), 54 deletions(-)

diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index 9b73f6826cf..8984cf8cdcd 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -11,19 +11,25 @@ module API
         else milestones
         end
       end
+
+      params :optional_params do
+        optional :description, type: String, desc: 'The description of the milestone'
+        optional :due_date, type: String, desc: 'The due date of the milestone'
+      end
     end
 
+    params do
+      requires :id, type: String, desc: 'The ID of a project'
+    end
     resource :projects do
-      # Get a list of project milestones
-      #
-      # Parameters:
-      #   id (required)    - The ID of a project
-      #   state (optional) - Return "active" or "closed" milestones
-      # Example Request:
-      #   GET /projects/:id/milestones
-      #   GET /projects/:id/milestones?iid=42
-      #   GET /projects/:id/milestones?state=active
-      #   GET /projects/:id/milestones?state=closed
+      desc 'Get a list of project milestones' do
+        success Entities::Milestone
+      end
+      params do
+        optional :state, type: String, values: %w[active closed all], default: 'all',
+                         desc: 'Return "active", "closed", or "all" milestones'
+        optional :iid, type: Integer, desc: 'The IID of the milestone'
+      end
       get ":id/milestones" do
         authorize! :read_milestone, user_project
 
@@ -34,34 +40,31 @@ module API
         present paginate(milestones), with: Entities::Milestone
       end
 
-      # Get a single project milestone
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      #   milestone_id (required) - The ID of a project milestone
-      # Example Request:
-      #   GET /projects/:id/milestones/:milestone_id
+      desc 'Get a single project milestone' do
+        success Entities::Milestone
+      end
+      params do
+        requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+      end
       get ":id/milestones/:milestone_id" do
         authorize! :read_milestone, user_project
 
-        @milestone = user_project.milestones.find(params[:milestone_id])
-        present @milestone, with: Entities::Milestone
+        milestone = user_project.milestones.find(params[:milestone_id])
+        present milestone, with: Entities::Milestone
       end
 
-      # Create a new project milestone
-      #
-      # Parameters:
-      #   id (required) - The ID of the project
-      #   title (required) - The title of the milestone
-      #   description (optional) - The description of the milestone
-      #   due_date (optional) - The due date of the milestone
-      # Example Request:
-      #   POST /projects/:id/milestones
+      desc 'Create a new project milestone' do
+        success Entities::Milestone
+      end
+      params do
+        requires :title, type: String, desc: 'The title of the milestone'
+        use :optional_params
+      end
       post ":id/milestones" do
         authorize! :admin_milestone, user_project
-        required_attributes! [:title]
-        attrs = attributes_for_keys [:title, :description, :due_date]
-        milestone = ::Milestones::CreateService.new(user_project, current_user, attrs).execute
+        milestone_params = declared(params, include_parent_namespaces: false)
+
+        milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute
 
         if milestone.valid?
           present milestone, with: Entities::Milestone
@@ -70,22 +73,23 @@ module API
         end
       end
 
-      # Update an existing project milestone
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      #   milestone_id (required) - The ID of a project milestone
-      #   title (optional) - The title of a milestone
-      #   description (optional) - The description of a milestone
-      #   due_date (optional) - The due date of a milestone
-      #   state_event (optional) - The state event of the milestone (close|activate)
-      # Example Request:
-      #   PUT /projects/:id/milestones/:milestone_id
+      desc 'Update an existing project milestone' do
+        success Entities::Milestone
+      end
+      params do
+        requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+        optional :title, type: String, desc: 'The title of the milestone'
+        optional :state_event, type: String, values: %w[close activate],
+                               desc: 'The state event of the milestone '
+        use :optional_params
+        at_least_one_of :title, :description, :due_date, :state_event
+      end
       put ":id/milestones/:milestone_id" do
         authorize! :admin_milestone, user_project
-        attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
-        milestone = user_project.milestones.find(params[:milestone_id])
-        milestone = ::Milestones::UpdateService.new(user_project, current_user, attrs).execute(milestone)
+        milestone_params = declared(params, include_parent_namespaces: false, include_missing: false)
+
+        milestone = user_project.milestones.find(milestone_params.delete(:milestone_id))
+        milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone)
 
         if milestone.valid?
           present milestone, with: Entities::Milestone
@@ -94,21 +98,20 @@ module API
         end
       end
 
-      # Get all issues for a single project milestone
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      #   milestone_id (required) - The ID of a project milestone
-      # Example Request:
-      #   GET /projects/:id/milestones/:milestone_id/issues
+      desc 'Get all issues for a single project milestone' do
+        success Entities::Issue
+      end
+      params do
+        requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+      end
       get ":id/milestones/:milestone_id/issues" do
         authorize! :read_milestone, user_project
 
-        @milestone = user_project.milestones.find(params[:milestone_id])
+        milestone = user_project.milestones.find(params[:milestone_id])
 
         finder_params = {
           project_id: user_project.id,
-          milestone_title: @milestone.title
+          milestone_title: milestone.title
         }
 
         issues = IssuesFinder.new(current_user, finder_params).execute
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index dd192bea432..62327f64e50 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -123,6 +123,15 @@ describe API::API, api: true  do
       expect(json_response['title']).to eq('updated title')
     end
 
+    it 'removes a due date if nil is passed' do
+      milestone.update!(due_date: "2016-08-05")
+
+      put api("/projects/#{project.id}/milestones/#{milestone.id}", user), due_date: nil
+
+      expect(response).to have_http_status(200)
+      expect(json_response['due_date']).to be_nil
+    end
+
     it 'returns a 404 error if milestone id not found' do
       put api("/projects/#{project.id}/milestones/1234", user),
         title: 'updated title'
-- 
GitLab