members.rb 5.82 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
module API
  class Members < Grape::API
    before { authenticate! }

    helpers ::API::Helpers::MembersHelpers

    %w[group project].each do |source_type|
      resource source_type.pluralize do
        # Get a list of group/project members viewable by the authenticated user.
        #
        # Parameters:
        #   id (required) - The group/project ID
        #   query         - Query string
        #
        # Example Request:
        #   GET /groups/:id/members
        #   GET /projects/:id/members
        get ":id/members" do
          source = find_source(source_type, params[:id])

21
          members = source.members.includes(:user)
22
          members = members.joins(:user).merge(User.search(params[:query])) if params[:query]
23
          members = paginate(members)
24

25
          present members.map(&:user), with: Entities::Member, members: members
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
        end

        # Get a group/project member
        #
        # Parameters:
        #   id (required) - The group/project ID
        #   user_id (required) - The user ID of the member
        #
        # Example Request:
        #   GET /groups/:id/members/:user_id
        #   GET /projects/:id/members/:user_id
        get ":id/members/:user_id" do
          source = find_source(source_type, params[:id])

          members = source.members
          member = members.find_by!(user_id: params[:user_id])

          present member.user, with: Entities::Member, member: member
        end

        # Add a new group/project member
        #
        # Parameters:
        #   id (required) - The group/project ID
        #   user_id (required) - The user ID of the new member
        #   access_level (required) - A valid access level
        #
        # Example Request:
        #   POST /groups/:id/members
        #   POST /projects/:id/members
        post ":id/members" do
          source = find_source(source_type, params[:id])
          authorize_admin_source!(source_type, source)
          required_attributes! [:user_id, :access_level]

          access_requester = source.requesters.find_by(user_id: params[:user_id])
          if access_requester
            # We pass current_user = access_requester so that the requester doesn't
            # receive a "access denied" email
            ::Members::DestroyService.new(access_requester, access_requester.user).execute
          end

          member = source.members.find_by(user_id: params[:user_id])
69 70 71 72 73 74 75 76 77 78

          # This is to ensure back-compatibility but 409 behavior should be used
          # for both project and group members in 9.0!
          conflict!('Member already exists') if source_type == 'group' && member

          unless member
            source.add_user(params[:user_id], params[:access_level], current_user)
            member = source.members.find_by(user_id: params[:user_id])
          end

79 80 81
          if member
            present member.user, with: Entities::Member, member: member
          else
82 83 84 85 86 87 88 89 90
            # 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.
            member = source.members.build(attributes_for_keys([:user_id, :access_level]))
            member.valid? # populate the errors

            # This is to ensure back-compatibility but 400 behavior should be used
            # for all validation errors in 9.0!
            render_api_error!('Access level is not known', 422) if member.errors.key?(:access_level)
            render_validation_error!(member)
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
          end
        end

        # Update a group/project member
        #
        # Parameters:
        #   id (required) - The group/project ID
        #   user_id (required) - The user ID of the member
        #   access_level (required) - A valid access level
        #
        # Example Request:
        #   PUT /groups/:id/members/:user_id
        #   PUT /projects/:id/members/:user_id
        put ":id/members/:user_id" do
          source = find_source(source_type, params[:id])
          authorize_admin_source!(source_type, source)
          required_attributes! [:user_id, :access_level]

          member = source.members.find_by!(user_id: params[:user_id])

          if member.update_attributes(access_level: params[:access_level])
            present member.user, with: Entities::Member, member: member
          else
114 115 116
            # This is to ensure back-compatibility but 400 behavior should be used
            # for all validation errors in 9.0!
            render_api_error!('Access level is not known', 422) if member.errors.key?(:access_level)
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
            render_validation_error!(member)
          end
        end

        # Remove a group/project member
        #
        # Parameters:
        #   id (required) - The group/project ID
        #   user_id (required) - The user ID of the member
        #
        # Example Request:
        #   DELETE /groups/:id/members/:user_id
        #   DELETE /projects/:id/members/:user_id
        delete ":id/members/:user_id" do
          source = find_source(source_type, params[:id])
          required_attributes! [:user_id]

134 135 136 137 138 139 140 141 142 143 144 145 146 147
          # This is to ensure back-compatibility but find_by! should be used
          # in that casse in 9.0!
          member = source.members.find_by(user_id: params[:user_id])

          # This is to ensure back-compatibility but this should be removed in
          # favor of find_by! in 9.0!
          not_found!("Member: user_id:#{params[:user_id]}") if source_type == 'group' && member.nil?

          # This is to ensure back-compatibility but 204 behavior should be used
          # for all DELETE endpoints in 9.0!
          if member.nil?
            { message: "Access revoked", id: params[:user_id].to_i }
          else
            ::Members::DestroyService.new(member, current_user).execute
148

149 150
            present member.user, with: Entities::Member, member: member
          end
151 152 153 154 155
        end
      end
    end
  end
end