Skip to content
Snippets Groups Projects
Commit 396f85e4 authored by Sean McGivern's avatar Sean McGivern
Browse files

Add expiration date to group memberships

parent 8b165628
No related branches found
No related tags found
No related merge requests found
Showing with 58 additions and 34 deletions
Loading
@@ -126,6 +126,7 @@
Loading
@@ -126,6 +126,7 @@
new NotificationsDropdown(); new NotificationsDropdown();
break; break;
case 'groups:group_members:index': case 'groups:group_members:index':
new MemberExpirationDate();
new GroupMembers(); new GroupMembers();
new UsersSelect(); new UsersSelect();
break; break;
Loading
Loading
Loading
@@ -21,7 +21,12 @@ class Groups::GroupMembersController < Groups::ApplicationController
Loading
@@ -21,7 +21,12 @@ class Groups::GroupMembersController < Groups::ApplicationController
end end
   
def create def create
@group.add_users(params[:user_ids].split(','), params[:access_level], current_user) @group.add_users(
params[:user_ids].split(','),
params[:access_level],
current_user: current_user,
expires_at: params[:expires_at]
)
   
redirect_to group_group_members_path(@group), notice: 'Users were successfully added.' redirect_to group_group_members_path(@group), notice: 'Users were successfully added.'
end end
Loading
@@ -63,7 +68,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
Loading
@@ -63,7 +68,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
protected protected
   
def member_params def member_params
params.require(:group_member).permit(:access_level, :user_id) params.require(:group_member).permit(:access_level, :user_id, :expires_at)
end end
   
# MembershipActions concern # MembershipActions concern
Loading
Loading
Loading
@@ -95,34 +95,40 @@ class Group < Namespace
Loading
@@ -95,34 +95,40 @@ class Group < Namespace
end end
end end
   
def add_users(user_ids, access_level, current_user = nil) def add_users(user_ids, access_level, current_user: nil, expires_at: nil)
user_ids.each do |user_id| user_ids.each do |user_id|
Member.add_user(self.group_members, user_id, access_level, current_user: current_user) Member.add_user(
self.group_members,
user_id,
access_level,
current_user: current_user,
expires_at: expires_at
)
end end
end end
   
def add_user(user, access_level, current_user = nil) def add_user(user, access_level, current_user: nil, expires_at: nil)
add_users([user], access_level, current_user) add_users([user], access_level, current_user: current_user, expires_at: expires_at)
end end
   
def add_guest(user, current_user = nil) def add_guest(user, current_user = nil)
add_user(user, Gitlab::Access::GUEST, current_user) add_user(user, Gitlab::Access::GUEST, current_user: current_user)
end end
   
def add_reporter(user, current_user = nil) def add_reporter(user, current_user = nil)
add_user(user, Gitlab::Access::REPORTER, current_user) add_user(user, Gitlab::Access::REPORTER, current_user: current_user)
end end
   
def add_developer(user, current_user = nil) def add_developer(user, current_user = nil)
add_user(user, Gitlab::Access::DEVELOPER, current_user) add_user(user, Gitlab::Access::DEVELOPER, current_user: current_user)
end end
   
def add_master(user, current_user = nil) def add_master(user, current_user = nil)
add_user(user, Gitlab::Access::MASTER, current_user) add_user(user, Gitlab::Access::MASTER, current_user: current_user)
end end
   
def add_owner(user, current_user = nil) def add_owner(user, current_user = nil)
add_user(user, Gitlab::Access::OWNER, current_user) add_user(user, Gitlab::Access::OWNER, current_user: current_user)
end end
   
def has_owner?(user) def has_owner?(user)
Loading
Loading
Loading
@@ -1003,8 +1003,8 @@ class Project < ActiveRecord::Base
Loading
@@ -1003,8 +1003,8 @@ class Project < ActiveRecord::Base
project_members.find_by(user_id: user) project_members.find_by(user_id: user)
end end
   
def add_user(user, access_level, current_user = nil) def add_user(user, access_level, current_user: nil, expires_at: nil)
team.add_user(user, access_level, current_user) team.add_user(user, access_level, current_user: current_user, expires_at: expires_at)
end end
   
def default_branch def default_branch
Loading
Loading
Loading
@@ -17,7 +17,7 @@ class ProjectTeam
Loading
@@ -17,7 +17,7 @@ class ProjectTeam
if users.respond_to?(:each) if users.respond_to?(:each)
add_users(users, access, current_user: current_user) add_users(users, access, current_user: current_user)
else else
add_user(users, access, current_user) add_user(users, access, current_user: current_user)
end end
end end
   
Loading
@@ -43,8 +43,8 @@ class ProjectTeam
Loading
@@ -43,8 +43,8 @@ class ProjectTeam
) )
end end
   
def add_user(user, access, current_user = nil) def add_user(user, access, current_user: nil, expires_at: nil)
add_users([user], access, current_user: current_user) add_users([user], access, current_user: current_user, expires_at: expires_at)
end end
   
# Remove all users from project team # Remove all users from project team
Loading
Loading
Loading
@@ -14,5 +14,12 @@
Loading
@@ -14,5 +14,12 @@
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("user/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
   
.form-group
= f.label :expires_at, 'Access expiration date', class: 'control-label'
.col-sm-10
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
%i.clear-icon.js-clear-input
.form-actions .form-actions
= f.submit 'Add users to group', class: "btn btn-create" = f.submit 'Add users to group', class: "btn btn-create"
:plain :plain
$("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}');
new MemberExpirationDate();
Loading
@@ -82,12 +82,11 @@
Loading
@@ -82,12 +82,11 @@
= label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label' = label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label'
.col-sm-10 .col-sm-10
= f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}" = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}"
- if member.is_a?(ProjectMember) .form-group
.form-group = label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label'
= label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label' .col-sm-10
.col-sm-10 .clearable-input
.clearable-input = f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}"
= f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}" %i.clear-icon.js-clear-input
%i.clear-icon.js-clear-input
.prepend-top-10 .prepend-top-10
= f.submit 'Save', class: 'btn btn-save btn-sm' = f.submit 'Save', class: 'btn btn-save btn-sm'
Loading
@@ -117,7 +117,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
Loading
@@ -117,7 +117,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
   
page.within "#group_member_#{member.id}" do page.within "#group_member_#{member.id}" do
click_button 'Edit' click_button 'Edit'
select 'Developer', from: 'group_member_access_level' select 'Developer', from: "member_access_level_#{member.id}"
click_on 'Save' click_on 'Save'
end end
end end
Loading
Loading
Loading
@@ -66,7 +66,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
Loading
@@ -66,7 +66,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
project_member = project.project_members.find_by(user_id: user.id) project_member = project.project_members.find_by(user_id: user.id)
page.within "#project_member_#{project_member.id}" do page.within "#project_member_#{project_member.id}" do
click_button 'Edit' click_button 'Edit'
select "Reporter", from: "project_member_access_level" select "Reporter", from: "member_access_level_#{project_member.id}"
click_button "Save" click_button "Save"
end end
end end
Loading
Loading
Loading
@@ -97,6 +97,10 @@ module API
Loading
@@ -97,6 +97,10 @@ module API
member = options[:member] || options[:members].find { |m| m.user_id == user.id } member = options[:member] || options[:members].find { |m| m.user_id == user.id }
member.access_level member.access_level
end end
expose :expires_at do |user, options|
member = options[:member] || options[:members].find { |m| m.user_id == user.id }
member.expires_at
end
end end
   
class AccessRequester < UserBasic class AccessRequester < UserBasic
Loading
@@ -104,9 +108,6 @@ module API
Loading
@@ -104,9 +108,6 @@ module API
access_requester = options[:access_requester] || options[:access_requesters].find { |m| m.user_id == user.id } access_requester = options[:access_requester] || options[:access_requesters].find { |m| m.user_id == user.id }
access_requester.requested_at access_requester.requested_at
end end
expose :expires_at do |user, options|
options[:project].project_members.find_by(user_id: user.id).expires_at
end
end end
   
class Group < Grape::Entity class Group < Grape::Entity
Loading
Loading
Loading
@@ -49,6 +49,7 @@ module API
Loading
@@ -49,6 +49,7 @@ module API
# id (required) - The group/project ID # id (required) - The group/project ID
# user_id (required) - The user ID of the new member # user_id (required) - The user ID of the new member
# access_level (required) - A valid access level # access_level (required) - A valid access level
# expires_at (optional) - Date string in the format YEAR-MONTH-DAY
# #
# Example Request: # Example Request:
# POST /groups/:id/members # POST /groups/:id/members
Loading
@@ -72,7 +73,7 @@ module API
Loading
@@ -72,7 +73,7 @@ module API
conflict!('Member already exists') if source_type == 'group' && member conflict!('Member already exists') if source_type == 'group' && member
   
unless member unless member
source.add_user(params[:user_id], params[:access_level], current_user) source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at])
member = source.members.find_by(user_id: params[:user_id]) member = source.members.find_by(user_id: params[:user_id])
end end
   
Loading
@@ -81,7 +82,7 @@ module API
Loading
@@ -81,7 +82,7 @@ module API
else else
# Since `source.add_user` doesn't return a member object, we have to # Since `source.add_user` doesn't return a member object, we have to
# build a new one and populate its errors in order to render them. # build a new one and populate its errors in order to render them.
member = source.members.build(attributes_for_keys([:user_id, :access_level])) member = source.members.build(attributes_for_keys([:user_id, :access_level, :expires_at]))
member.valid? # populate the errors member.valid? # populate the errors
   
# This is to ensure back-compatibility but 400 behavior should be used # This is to ensure back-compatibility but 400 behavior should be used
Loading
@@ -97,6 +98,7 @@ module API
Loading
@@ -97,6 +98,7 @@ module API
# id (required) - The group/project ID # id (required) - The group/project ID
# user_id (required) - The user ID of the member # user_id (required) - The user ID of the member
# access_level (required) - A valid access level # access_level (required) - A valid access level
# expires_at (optional) - Date string in the format YEAR-MONTH-DAY
# #
# Example Request: # Example Request:
# PUT /groups/:id/members/:user_id # PUT /groups/:id/members/:user_id
Loading
@@ -107,7 +109,7 @@ module API
Loading
@@ -107,7 +109,7 @@ module API
required_attributes! [:user_id, :access_level] required_attributes! [:user_id, :access_level]
   
member = source.members.find_by!(user_id: params[:user_id]) member = source.members.find_by!(user_id: params[:user_id])
attrs = attributes_for_keys [:access_level] attrs = attributes_for_keys [:access_level, :expires_at]
   
if member.update_attributes(attrs) if member.update_attributes(attrs)
present member.user, with: Entities::Member, member: member present member.user, with: Entities::Member, member: member
Loading
Loading
Loading
@@ -19,7 +19,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
Loading
@@ -19,7 +19,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
   
page.within '.users-project-form' do page.within '.users-project-form' do
select2(new_member.id, from: '#user_ids', multiple: true) select2(new_member.id, from: '#user_ids', multiple: true)
fill_in 'Access expiration date', with: '2016-08-10' fill_in 'expires_at', with: '2016-08-10'
click_on 'Add users to project' click_on 'Add users to project'
end end
   
Loading
Loading
Loading
@@ -122,12 +122,13 @@ describe API::Members, api: true do
Loading
@@ -122,12 +122,13 @@ describe API::Members, api: true do
it 'creates a new member' do it 'creates a new member' do
expect do expect do
post api("/#{source_type.pluralize}/#{source.id}/members", master), post api("/#{source_type.pluralize}/#{source.id}/members", master),
user_id: stranger.id, access_level: Member::DEVELOPER user_id: stranger.id, access_level: Member::DEVELOPER, expires_at: '2016-08-05'
   
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
end.to change { source.members.count }.by(1) end.to change { source.members.count }.by(1)
expect(json_response['id']).to eq(stranger.id) expect(json_response['id']).to eq(stranger.id)
expect(json_response['access_level']).to eq(Member::DEVELOPER) expect(json_response['access_level']).to eq(Member::DEVELOPER)
expect(json_response['expires_at']).to eq('2016-08-05')
end end
end end
   
Loading
@@ -183,11 +184,12 @@ describe API::Members, api: true do
Loading
@@ -183,11 +184,12 @@ describe API::Members, api: true do
context 'when authenticated as a master/owner' do context 'when authenticated as a master/owner' do
it 'updates the member' do it 'updates the member' do
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master), put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
access_level: Member::MASTER access_level: Member::MASTER, expires_at: '2016-08-05'
   
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['id']).to eq(developer.id) expect(json_response['id']).to eq(developer.id)
expect(json_response['access_level']).to eq(Member::MASTER) expect(json_response['access_level']).to eq(Member::MASTER)
expect(json_response['expires_at']).to eq('2016-08-05')
end end
end end
   
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment