From 036cc8c27e8340a3eed63444bd3f42f86037f350 Mon Sep 17 00:00:00 2001
From: Robert Schilling <rschilling@student.tugraz.at>
Date: Fri, 15 Jul 2016 16:21:53 +0200
Subject: [PATCH] API: Expose issue#confidential

---
 CHANGELOG                        |  1 +
 doc/api/issues.md                | 32 +++++++++++-----
 lib/api/entities.rb              |  1 +
 lib/api/issues.rb                | 14 ++++++-
 spec/requests/api/issues_spec.rb | 65 +++++++++++++++++++++++++++++++-
 5 files changed, 100 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 4e963702b8e..ec76455e837 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,6 +14,7 @@ v 8.12.0 (unreleased)
   - Add hover color to emoji icon (ClemMakesApps)
   - Fix branches page dropdown sort alignment (ClemMakesApps)
   - Add white background for no readme container (ClemMakesApps)
+  - API: Expose issue confidentiality flag. (Robert Schilling)
   - Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
   - Add `wiki_page_events` to project hook APIs (Ben Boeckel)
   - Remove Gitorious import
diff --git a/doc/api/issues.md b/doc/api/issues.md
index b194799ccbf..eed0d2fce51 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -80,7 +80,8 @@ Example response:
       "subscribed" : false,
       "user_notes_count": 1,
       "due_date": "2016-07-22",
-      "web_url": "http://example.com/example/example/issues/6"
+      "web_url": "http://example.com/example/example/issues/6",
+      "confidential": false
    }
 ]
 ```
@@ -158,7 +159,8 @@ Example response:
       "subscribed" : false,
       "user_notes_count": 1,
       "due_date": null,
-      "web_url": "http://example.com/example/example/issues/1"
+      "web_url": "http://example.com/example/example/issues/1",
+      "confidential": false
    }
 ]
 ```
@@ -238,7 +240,8 @@ Example response:
       "subscribed" : false,
       "user_notes_count": 1,
       "due_date": "2016-07-22",
-      "web_url": "http://example.com/example/example/issues/1"
+      "web_url": "http://example.com/example/example/issues/1",
+      "confidential": false
    }
 ]
 ```
@@ -303,7 +306,8 @@ Example response:
    "subscribed": false,
    "user_notes_count": 1,
    "due_date": null,
-   "web_url": "http://example.com/example/example/issues/1"
+   "web_url": "http://example.com/example/example/issues/1",
+   "confidential": false
 }
 ```
 
@@ -324,6 +328,7 @@ POST /projects/:id/issues
 | `id`            | integer | yes | The ID of a project |
 | `title`         | string  | yes | The title of an issue |
 | `description`   | string  | no  | The description of an issue  |
+| `confidential`  | boolean | no  | Set an issue to be confidential. Default is `false`.  |
 | `assignee_id`   | integer | no  | The ID of a user to assign issue |
 | `milestone_id`  | integer | no  | The ID of a milestone to assign issue |
 | `labels`        | string  | no  | Comma-separated label names for an issue  |
@@ -362,7 +367,8 @@ Example response:
    "subscribed" : true,
    "user_notes_count": 0,
    "due_date": null,
-   "web_url": "http://example.com/example/example/issues/14"
+   "web_url": "http://example.com/example/example/issues/14",
+   "confidential": false
 }
 ```
 
@@ -385,6 +391,7 @@ PUT /projects/:id/issues/:issue_id
 | `issue_id`      | integer | yes | The ID of a project's issue |
 | `title`         | string  | no  | The title of an issue |
 | `description`   | string  | no  | The description of an issue  |
+| `confidential`  | boolean | no  | Updates an issue to be confidential |
 | `assignee_id`   | integer | no  | The ID of a user to assign the issue to |
 | `milestone_id`  | integer | no  | The ID of a milestone to assign the issue to |
 | `labels`        | string  | no  | Comma-separated label names for an issue  |
@@ -424,7 +431,8 @@ Example response:
    "subscribed" : true,
    "user_notes_count": 0,
    "due_date": "2016-07-22",
-   "web_url": "http://example.com/example/example/issues/15"
+   "web_url": "http://example.com/example/example/issues/15",
+   "confidential": false
 }
 ```
 
@@ -503,7 +511,8 @@ Example response:
     "web_url": "https://gitlab.example.com/u/solon.cremin"
   },
   "due_date": null,
-  "web_url": "http://example.com/example/example/issues/11"
+  "web_url": "http://example.com/example/example/issues/11",
+  "confidential": false
 }
 ```
 
@@ -559,7 +568,8 @@ Example response:
     "web_url": "https://gitlab.example.com/u/solon.cremin"
   },
   "due_date": null,
-  "web_url": "http://example.com/example/example/issues/11"
+  "web_url": "http://example.com/example/example/issues/11",
+  "confidential": false
 }
 ```
 
@@ -616,7 +626,8 @@ Example response:
   },
   "subscribed": false,
   "due_date": null,
-  "web_url": "http://example.com/example/example/issues/12"
+  "web_url": "http://example.com/example/example/issues/12",
+  "confidential": false
 }
 ```
 
@@ -704,7 +715,8 @@ Example response:
     "upvotes": 0,
     "downvotes": 0,
     "due_date": null,
-    "web_url": "http://example.com/example/example/issues/110"
+    "web_url": "http://example.com/example/example/issues/110",
+    "confidential": false
   },
   "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/issues/10",
   "body": "Vel voluptas atque dicta mollitia adipisci qui at.",
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 4335e3055ef..e3a8ff6de80 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -211,6 +211,7 @@ module API
       expose :user_notes_count
       expose :upvotes, :downvotes
       expose :due_date
+      expose :confidential
 
       expose :web_url do |issue, options|
         Gitlab::UrlBuilder.build(issue)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index d0bc7243e54..556684187d8 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -140,12 +140,13 @@ module API
       #   labels (optional)       - The labels of an issue
       #   created_at (optional)   - Date time string, ISO 8601 formatted
       #   due_date (optional)     - Date time string in the format YEAR-MONTH-DAY
+      #   confidential (optional) - Boolean parameter if the issue should be confidential
       # Example Request:
       #   POST /projects/:id/issues
       post ':id/issues' do
         required_attributes! [:title]
 
-        keys = [:title, :description, :assignee_id, :milestone_id, :due_date]
+        keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential]
         keys << :created_at if current_user.admin? || user_project.owner == current_user
         attrs = attributes_for_keys(keys)
 
@@ -156,6 +157,10 @@ module API
 
         attrs[:labels] = params[:labels] if params[:labels]
 
+        # Convert and filter out invalid confidential flags
+        attrs['confidential'] = to_boolean(attrs['confidential'])
+        attrs.delete('confidential') if attrs['confidential'].nil?
+
         issue = ::Issues::CreateService.new(user_project, current_user, attrs.merge(request: request, api: true)).execute
 
         if issue.spam?
@@ -182,12 +187,13 @@ module API
       #   state_event (optional) - The state event of an issue (close|reopen)
       #   updated_at (optional) - Date time string, ISO 8601 formatted
       #   due_date (optional)     - Date time string in the format YEAR-MONTH-DAY
+      #   confidential (optional) - Boolean parameter if the issue should be confidential
       # Example Request:
       #   PUT /projects/:id/issues/:issue_id
       put ':id/issues/:issue_id' do
         issue = user_project.issues.find(params[:issue_id])
         authorize! :update_issue, issue
-        keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date]
+        keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date, :confidential]
         keys << :updated_at if current_user.admin? || user_project.owner == current_user
         attrs = attributes_for_keys(keys)
 
@@ -198,6 +204,10 @@ module API
 
         attrs[:labels] = params[:labels] if params[:labels]
 
+        # Convert and filter out invalid confidential flags
+        attrs['confidential'] = to_boolean(attrs['confidential'])
+        attrs.delete('confidential') if attrs['confidential'].nil?
+
         issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
 
         if issue.valid?
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 3362a88d798..47344a13b5e 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -405,6 +405,7 @@ describe API::API, api: true  do
       expect(json_response['milestone']).to be_a Hash
       expect(json_response['assignee']).to be_a Hash
       expect(json_response['author']).to be_a Hash
+      expect(json_response['confidential']).to be_falsy
     end
 
     it "returns a project issue by id" do
@@ -470,13 +471,51 @@ describe API::API, api: true  do
   end
 
   describe "POST /projects/:id/issues" do
-    it "creates a new project issue" do
+    it 'creates a new project issue' do
       post api("/projects/#{project.id}/issues", user),
         title: 'new issue', labels: 'label, label2'
+
       expect(response).to have_http_status(201)
       expect(json_response['title']).to eq('new issue')
       expect(json_response['description']).to be_nil
       expect(json_response['labels']).to eq(['label', 'label2'])
+      expect(json_response['confidential']).to be_falsy
+    end
+
+    it 'creates a new confidential project issue' do
+      post api("/projects/#{project.id}/issues", user),
+        title: 'new issue', confidential: true
+
+      expect(response).to have_http_status(201)
+      expect(json_response['title']).to eq('new issue')
+      expect(json_response['confidential']).to be_truthy
+    end
+
+    it 'creates a new confidential project issue with a different param' do
+      post api("/projects/#{project.id}/issues", user),
+        title: 'new issue', confidential: 'y'
+
+      expect(response).to have_http_status(201)
+      expect(json_response['title']).to eq('new issue')
+      expect(json_response['confidential']).to be_truthy
+    end
+
+    it 'creates a public issue when confidential param is false' do
+      post api("/projects/#{project.id}/issues", user),
+        title: 'new issue', confidential: false
+
+      expect(response).to have_http_status(201)
+      expect(json_response['title']).to eq('new issue')
+      expect(json_response['confidential']).to be_falsy
+    end
+
+    it 'creates a public issue when confidential param is invalid' do
+      post api("/projects/#{project.id}/issues", user),
+        title: 'new issue', confidential: 'foo'
+
+      expect(response).to have_http_status(201)
+      expect(json_response['title']).to eq('new issue')
+      expect(json_response['confidential']).to be_falsy
     end
 
     it "sends notifications for subscribers of newly added labels" do
@@ -632,6 +671,30 @@ describe API::API, api: true  do
         expect(response).to have_http_status(200)
         expect(json_response['title']).to eq('updated title')
       end
+
+      it 'sets an issue to confidential' do
+        put api("/projects/#{project.id}/issues/#{issue.id}", user),
+          confidential: true
+
+        expect(response).to have_http_status(200)
+        expect(json_response['confidential']).to be_truthy
+      end
+
+      it 'makes a confidential issue public' do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
+          confidential: false
+
+        expect(response).to have_http_status(200)
+        expect(json_response['confidential']).to be_falsy
+      end
+
+      it 'does not update a confidential issue with wrong confidential flag' do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
+          confidential: 'foo'
+
+        expect(response).to have_http_status(200)
+        expect(json_response['confidential']).to be_truthy
+      end
     end
   end
 
-- 
GitLab