Commit 3a59efd5 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '19359-api-for-notification-settings' into 'master'

Add notification_settings API calls

## What does this MR do?
It adds API calls to get/update notification settings, globally and at the group/project level.

## Are there points in the code the reviewer needs to double check?
None

## Why was this MR needed?
Notification settings were updated, but related API calls were never created.

## What are the relevant issue numbers?
gitlab-org/gitlab-ce#19359

## Does this MR meet the acceptance criteria?

- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- [x] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)
- [x] API support added
- Tests
  - [x] Added for this feature/bug
  - [x] All builds are passing
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

Closes #19359

See merge request !5632
parents 13bb9ed7 48333b9d
......@@ -116,6 +116,7 @@ v 8.12.0 (unreleased)
- Use default clone protocol on "check out, review, and merge locally" help page URL
- API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
- Allow bulk update merge requests from merge requests index page
- Add notification_settings API calls !5632 (mahcsig)
v 8.11.6 (unreleased)
......
......@@ -27,6 +27,7 @@ following locations:
- [Open source license templates](licenses.md)
- [Namespaces](namespaces.md)
- [Notes](notes.md) (comments)
- [Notification settings](notification_settings.md)
- [Pipelines](pipelines.md)
- [Projects](projects.md) including setting Webhooks
- [Project Access Requests](access_requests.md)
......
# Notification settings
>**Note:** This feature was [introduced][ce-5632] in GitLab 8.12.
**Valid notification levels**
The notification levels are defined in the `NotificationSetting::level` model enumeration. Currently, these levels are recognized:
```
disabled
participating
watch
global
mention
custom
```
If the `custom` level is used, specific email events can be controlled. Notification email events are defined in the `NotificationSetting::EMAIL_EVENTS` model variable. Currently, these events are recognized:
```
new_note
new_issue
reopen_issue
close_issue
reassign_issue
new_merge_request
reopen_merge_request
close_merge_request
reassign_merge_request
merge_merge_request
```
## Global notification settings
Get current notification settings and email address.
```
GET /notification_settings
```
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/notification_settings
```
Example response:
```json
{
"level": "participating",
"notification_email": "admin@example.com"
}
```
## Update global notification settings
Update current notification settings and email address.
```
PUT /notification_settings
```
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/notification_settings?level=watch
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `level` | string | no | The global notification level |
| `notification_email` | string | no | The email address to send notifications |
| `new_note` | boolean | no | Enable/disable this notification |
| `new_issue` | boolean | no | Enable/disable this notification |
| `reopen_issue` | boolean | no | Enable/disable this notification |
| `close_issue` | boolean | no | Enable/disable this notification |
| `reassign_issue` | boolean | no | Enable/disable this notification |
| `new_merge_request` | boolean | no | Enable/disable this notification |
| `reopen_merge_request` | boolean | no | Enable/disable this notification |
| `close_merge_request` | boolean | no | Enable/disable this notification |
| `reassign_merge_request` | boolean | no | Enable/disable this notification |
| `merge_merge_request` | boolean | no | Enable/disable this notification |
Example response:
```json
{
"level": "watch",
"notification_email": "admin@example.com"
}
```
## Group / project level notification settings
Get current group or project notification settings.
```
GET /groups/:id/notification_settings
GET /projects/:id/notification_settings
```
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/5/notification_settings
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/notification_settings
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The group/project ID or path |
Example response:
```json
{
"level": "global"
}
```
## Update group/project level notification settings
Update current group/project notification settings.
```
PUT /groups/:id/notification_settings
PUT /projects/:id/notification_settings
```
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/5/notification_settings?level=watch
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/notification_settings?level=custom&new_note=true
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The group/project ID or path |
| `level` | string | no | The global notification level |
| `new_note` | boolean | no | Enable/disable this notification |
| `new_issue` | boolean | no | Enable/disable this notification |
| `reopen_issue` | boolean | no | Enable/disable this notification |
| `close_issue` | boolean | no | Enable/disable this notification |
| `reassign_issue` | boolean | no | Enable/disable this notification |
| `new_merge_request` | boolean | no | Enable/disable this notification |
| `reopen_merge_request` | boolean | no | Enable/disable this notification |
| `close_merge_request` | boolean | no | Enable/disable this notification |
| `reassign_merge_request` | boolean | no | Enable/disable this notification |
| `merge_merge_request` | boolean | no | Enable/disable this notification |
Example responses:
```json
{
"level": "watch"
}
{
"level": "custom",
"events": {
"new_note": true,
"new_issue": false,
"reopen_issue": false,
"close_issue": false,
"reassign_issue": false,
"new_merge_request": false,
"reopen_merge_request": false,
"close_merge_request": false,
"reassign_merge_request": false,
"merge_merge_request": false
}
}
```
[ce-5632]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5632
......@@ -51,6 +51,7 @@ module API
mount ::API::Milestones
mount ::API::Namespaces
mount ::API::Notes
mount ::API::NotificationSettings
mount ::API::Pipelines
mount ::API::ProjectHooks
mount ::API::ProjectSnippets
......
......@@ -375,7 +375,7 @@ module API
expose :access_level
expose :notification_level do |member, options|
if member.notification_setting
NotificationSetting.levels[member.notification_setting.level]
::NotificationSetting.levels[member.notification_setting.level]
end
end
end
......@@ -386,6 +386,21 @@ module API
class GroupAccess < MemberAccess
end
class NotificationSetting < Grape::Entity
expose :level
expose :events, if: ->(notification_setting, _) { notification_setting.custom? } do
::NotificationSetting::EMAIL_EVENTS.each do |event|
expose event
end
end
end
class GlobalNotificationSetting < NotificationSetting
expose :notification_email do |notification_setting, options|
notification_setting.user.notification_email
end
end
class ProjectService < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active
expose :push_events, :issues_events, :merge_requests_events
......
module API
# notification_settings API
class NotificationSettings < Grape::API
before { authenticate! }
helpers ::API::Helpers::MembersHelpers
resource :notification_settings do
desc 'Get global notification level settings and email, defaults to Participate' do
detail 'This feature was introduced in GitLab 8.12'
success Entities::GlobalNotificationSetting
end
get do
notification_setting = current_user.global_notification_setting
present notification_setting, with: Entities::GlobalNotificationSetting
end
desc 'Update global notification level settings and email, defaults to Participate' do
detail 'This feature was introduced in GitLab 8.12'
success Entities::GlobalNotificationSetting
end
params do
optional :level, type: String, desc: 'The global notification level'
optional :notification_email, type: String, desc: 'The email address to send notifications'
NotificationSetting::EMAIL_EVENTS.each do |event|
optional event, type: Boolean, desc: 'Enable/disable this notification'
end
end
put do
notification_setting = current_user.global_notification_setting
begin
notification_setting.transaction do
new_notification_email = params.delete(:notification_email)
declared_params = declared(params, include_missing: false).to_h
current_user.update(notification_email: new_notification_email) if new_notification_email
notification_setting.update(declared_params)
end
rescue ArgumentError => e # catch level enum error
render_api_error! e.to_s, 400
end
render_validation_error! current_user
render_validation_error! notification_setting
present notification_setting, with: Entities::GlobalNotificationSetting
end
end
%w[group project].each do |source_type|
resource source_type.pluralize do
desc "Get #{source_type} level notification level settings, defaults to Global" do
detail 'This feature was introduced in GitLab 8.12'
success Entities::NotificationSetting
end
params do
requires :id, type: String, desc: 'The group ID or project ID or project NAMESPACE/PROJECT_NAME'
end
get ":id/notification_settings" do
source = find_source(source_type, params[:id])
notification_setting = current_user.notification_settings_for(source)
present notification_setting, with: Entities::NotificationSetting
end
desc "Update #{source_type} level notification level settings, defaults to Global" do
detail 'This feature was introduced in GitLab 8.12'
success Entities::NotificationSetting
end
params do
requires :id, type: String, desc: 'The group ID or project ID or project NAMESPACE/PROJECT_NAME'
optional :level, type: String, desc: "The #{source_type} notification level"
NotificationSetting::EMAIL_EVENTS.each do |event|
optional event, type: Boolean, desc: 'Enable/disable this notification'
end
end
put ":id/notification_settings" do
source = find_source(source_type, params.delete(:id))
notification_setting = current_user.notification_settings_for(source)
begin
declared_params = declared(params, include_missing: false).to_h
notification_setting.update(declared_params)
rescue ArgumentError => e # catch level enum error
render_api_error! e.to_s, 400
end
render_validation_error! notification_setting
present notification_setting, with: Entities::NotificationSetting
end
end
end
end
end
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: group) }
describe "GET /notification_settings" do
it "returns global notification settings for the current user" do
get api("/notification_settings", user)
expect(response).to have_http_status(200)
expect(json_response).to be_a Hash
expect(json_response['notification_email']).to eq(user.notification_email)
expect(json_response['level']).to eq(user.global_notification_setting.level)
end
end
describe "PUT /notification_settings" do
let(:email) { create(:email, user: user) }
it "updates global notification settings for the current user" do
put api("/notification_settings", user), { level: 'watch', notification_email: email.email }
expect(response).to have_http_status(200)
expect(json_response['notification_email']).to eq(email.email)
expect(user.reload.notification_email).to eq(email.email)
expect(json_response['level']).to eq(user.reload.global_notification_setting.level)
end
end
describe "PUT /notification_settings" do
it "fails on non-user email address" do
put api("/notification_settings", user), { notification_email: 'invalid@example.com' }
expect(response).to have_http_status(400)
end
end
describe "GET /groups/:id/notification_settings" do
it "returns group level notification settings for the current user" do
get api("/groups/#{group.id}/notification_settings", user)
expect(response).to have_http_status(200)
expect(json_response).to be_a Hash
expect(json_response['level']).to eq(user.notification_settings_for(group).level)
end
end
describe "PUT /groups/:id/notification_settings" do
it "updates group level notification settings for the current user" do
put api("/groups/#{group.id}/notification_settings", user), { level: 'watch' }
expect(response).to have_http_status(200)
expect(json_response['level']).to eq(user.reload.notification_settings_for(group).level)
end
end
describe "GET /projects/:id/notification_settings" do
it "returns project level notification settings for the current user" do
get api("/projects/#{project.id}/notification_settings", user)
expect(response).to have_http_status(200)
expect(json_response).to be_a Hash
expect(json_response['level']).to eq(user.notification_settings_for(project).level)
end
end
describe "PUT /projects/:id/notification_settings" do
it "updates project level notification settings for the current user" do
put api("/projects/#{project.id}/notification_settings", user), { level: 'custom', new_note: true }
expect(response).to have_http_status(200)
expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level)
expect(json_response['events']['new_note']).to eq(true)
expect(json_response['events']['new_issue']).to eq(false)
end
end
describe "PUT /projects/:id/notification_settings" do
it "fails on invalid level" do
put api("/projects/#{project.id}/notification_settings", user), { level: 'invalid' }
expect(response).to have_http_status(400)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment