Commit bd4c83b8 authored by Doug Stull's avatar Doug Stull Committed by Kerri Miller

Allow Invitations API to receive user_ids

- enhance to allow eventual deprecation of members API use in
  invite modal

Changelog: added
parent 683c0926
......@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Invitations API **(FREE)**
Use the Invitations API to send email to users you want to join a group or project, and to list pending
Use the Invitations API to invite or add users to a group or project, and to list pending
invitations.
## Valid access levels
......@@ -26,9 +26,9 @@ WARNING:
Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/219299),
projects in personal namespaces don't show owner (`50`) permission.
## Invite by email to group or project
## Add a member to a group or project
Invites a new user by email to join a group or project.
Adds a new member. You can specify a user ID or invite a user by email.
```plaintext
POST /groups/:id/invitations
......@@ -38,7 +38,8 @@ POST /projects/:id/invitations
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](index.md#namespaced-path-encoding) owned by the authenticated user |
| `email` | string | yes | The email of the new member or multiple emails separated by commas |
| `email` | string | yes (if `user_id` isn't provided) | The email of the new member or multiple emails separated by commas. |
| `user_id` | integer/string | yes (if `email` isn't provided) | The ID of the new member or multiple IDs separated by commas. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/350999) in GitLab 14.10. |
| `access_level` | integer | yes | A valid access level |
| `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY |
| `invite_source` | string | no | The source of the invitation that starts the member creation process. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/327120). |
......@@ -47,9 +48,9 @@ POST /projects/:id/invitations
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
--data "email=test@example.com&access_level=30" "https://gitlab.example.com/api/v4/groups/:id/invitations"
--data "email=test@example.com&user_id=1&access_level=30" "https://gitlab.example.com/api/v4/groups/:id/invitations"
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
--data "email=test@example.com&access_level=30" "https://gitlab.example.com/api/v4/projects/:id/invitations"
--data "email=test@example.com&user_id=1&access_level=30" "https://gitlab.example.com/api/v4/projects/:id/invitations"
```
Example responses:
......@@ -67,7 +68,8 @@ When there was any error sending the email:
"status": "error",
"message": {
"test@example.com": "Invite email has already been taken",
"test2@example.com": "User already exists in source"
"test2@example.com": "User already exists in source",
"test_username": "Access level is not included in the list"
}
}
```
......@@ -77,7 +79,7 @@ When there was any error sending the email:
Gets a list of invited group or project members viewable by the authenticated user.
Returns invitations to direct members only, and not through inherited ancestors' groups.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
This function takes pagination parameters `page` and `per_page` to restrict the list of members.
```plaintext
GET /groups/:id/invitations
......
......@@ -20,8 +20,9 @@ module API
success Entities::Invitation
end
params do
requires :email, types: [String, Array[String]], email_or_email_list: true, desc: 'The email address to invite, or multiple emails separated by comma'
requires :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)'
optional :email, types: [String, Array[String]], email_or_email_list: true, desc: 'The email address to invite, or multiple emails separated by comma'
optional :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api'
optional :tasks_to_be_done, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Tasks the inviter wants the member to do'
......@@ -30,11 +31,14 @@ module API
post ":id/invitations" do
::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/354016')
params[:source] = find_source(source_type, params[:id])
bad_request!('Must provide either email or user_id as a parameter') if params[:email].blank? && params[:user_id].blank?
authorize_admin_source!(source_type, params[:source])
source = find_source(source_type, params[:id])
authorize_admin_source!(source_type, source)
create_service_params = params.except(:user_id).merge({ user_ids: params[:user_id], source: source })
::Members::InviteService.new(current_user, params).execute
::Members::InviteService.new(current_user, create_service_params).execute
end
desc 'Get a list of group or project invitations viewable by the authenticated user' do
......
......@@ -100,8 +100,6 @@ module API
end
post ":id/members" do
::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/333434')
source = find_source(source_type, params[:id])
authorize_admin_source!(source_type, source)
......
......@@ -69,7 +69,7 @@ RSpec.describe API::Invitations do
end
end
it 'invites a new member' do
it 'adds a new member by email' do
expect do
post invitations_url(source, maintainer),
params: { email: email, access_level: Member::DEVELOPER }
......@@ -78,6 +78,24 @@ RSpec.describe API::Invitations do
end.to change { source.members.invite.count }.by(1)
end
it 'adds a new member by user_id' do
expect do
post invitations_url(source, maintainer),
params: { user_id: stranger.id, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.non_invite.count }.by(1)
end
it 'adds new members with email and user_id' do
expect do
post invitations_url(source, maintainer),
params: { email: email, user_id: stranger.id, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.invite.count }.by(1).and change { source.members.non_invite.count }.by(1)
end
it 'invites a list of new email addresses' do
expect do
email_list = [email, email2].join(',')
......@@ -88,6 +106,19 @@ RSpec.describe API::Invitations do
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.invite.count }.by(2)
end
it 'invites a list of new email addresses and user ids' do
expect do
stranger2 = create(:user)
email_list = [email, email2].join(',')
user_id_list = "#{stranger.id},#{stranger2.id}"
post invitations_url(source, maintainer),
params: { email: email_list, user_id: user_id_list, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.invite.count }.by(2).and change { source.members.non_invite.count }.by(2)
end
end
context 'access levels' do
......@@ -235,27 +266,36 @@ RSpec.describe API::Invitations do
expect(json_response['message'][developer.email]).to eq("User already exists in source")
end
it 'returns 404 when the email is not valid' do
it 'returns 400 when the invite params of email and user_id are not sent' do
post invitations_url(source, maintainer),
params: { access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq('400 Bad request - Must provide either email or user_id as a parameter')
end
it 'returns 400 when the email is blank' do
post invitations_url(source, maintainer),
params: { email: '', access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['message']).to eq('Invites cannot be blank')
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq('400 Bad request - Must provide either email or user_id as a parameter')
end
it 'returns 404 when the email list is not a valid format' do
it 'returns 400 when the user_id is blank' do
post invitations_url(source, maintainer),
params: { email: 'email1@example.com,not-an-email', access_level: Member::MAINTAINER }
params: { user_id: '', access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('email contains an invalid email address')
expect(json_response['message']).to eq('400 Bad request - Must provide either email or user_id as a parameter')
end
it 'returns 400 when email is not given' do
it 'returns 400 when the email list is not a valid format' do
post invitations_url(source, maintainer),
params: { access_level: Member::MAINTAINER }
params: { email: 'email1@example.com,not-an-email', access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('email contains an invalid email address')
end
it 'returns 400 when access_level is not given' do
......
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