Add member_role_id support to group protected branches REST API
## Context
| | |
|---|---|
| **Phase** | 3 of 6 |
| **Parallel with** | https://gitlab.com/gitlab-org/gitlab/-/work_items/594880+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594881+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594883+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594884+ |
| **Blocked by** | https://gitlab.com/gitlab-org/gitlab/-/work_items/594877+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594878+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594879+ |
| **Unblocks** | https://gitlab.com/gitlab-org/gitlab/-/work_items/594890+ |
## Summary
Verify and extend group-level protected branches API support for `member_role_id`, ensuring custom roles can be set when protecting branches at the group level.
## Background
The group protected branches API (`ee/lib/api/group_protected_branches.rb`) uses `use :optional_params_ee` which inherits from `ee/lib/ee/api/helpers/protected_branches_helpers.rb`. Once Issue 6 adds `member_role_id` to `shared_params`, the group API will automatically accept it in `allowed_to_push`, `allowed_to_merge`, and `allowed_to_unprotect` arrays.
However, the group API's `POST` endpoint also accepts scalar `push_access_level` and `merge_access_level` integer params (for simple single-role protection). Custom roles are **not** supported via these scalar params — only via the array form. This needs to be clearly documented and validated.
Additionally, group-level rules have a different namespace scoping concern: the `member_role` must belong to the same top-level group that owns the protected branch.
## Relevant files
- `ee/lib/api/group_protected_branches.rb` — group API
- `ee/lib/ee/api/helpers/protected_branches_helpers.rb` — shared params (updated in Issue 6)
- `ee/app/models/concerns/ee/protected_branch_access.rb` — namespace validation (Issue 4)
## Changes required
### Verify `use :optional_params_ee` inheritance
After Issue 6 is merged, write a quick integration test to confirm that `POST /groups/:id/protected_branches` with `allowed_to_merge: [{ member_role_id: 42 }]` correctly creates the access level row. No code change may be needed.
### Scalar param documentation
The `push_access_level` and `merge_access_level` scalar integer params on the group API `POST` endpoint accept integers (e.g. `40` for Maintainer). Add a comment/documentation note that custom roles are not supported via these params; use `allowed_to_push`/`allowed_to_merge` arrays instead.
### Namespace validation
The namespace validation added in Issue 4 checks `member_role.namespace_id == root_namespace.id`. For group-level protected branches, `protected_branch_group` is the group, and the root namespace is `protected_branch_group.root_ancestor`. Verify this correctly scopes to the right group in all cases (top-level group, subgroup's protected branch inherited from parent group).
### Group entity
Verify that `Entities::GroupProtectedBranch` uses the same `ProtectedRefAccess` entity (updated in Issue 7) so that `member_role_id` appears in group API responses as well.
## API examples
**Create group-level protected branch with custom role allowed to merge:**
```
POST /groups/:id/protected_branches
{
"name": "main",
"allowed_to_merge": [{ "member_role_id": 42 }],
"push_access_level": 40
}
```
**Response:**
```json
{
"id": 1,
"name": "main",
"merge_access_levels": [
{ "access_level": 30, "access_level_description": "Lead Developer", "member_role_id": 42 }
],
"push_access_levels": [
{ "access_level": 40, "access_level_description": "Maintainers" }
]
}
```
## Testing
- API request spec: `POST /groups/:id/protected_branches` with `allowed_to_merge: [{ member_role_id: N }]`
- API request spec: `PATCH /groups/:id/protected_branches/:name` updating to add/remove custom role access
- API request spec: `member_role_id` from a different namespace returns 422
- API request spec: scalar `push_access_level` param still works normally
- Verify group API response includes `member_role_id` in access level objects
## Dependencies
- Issue 6 (API params) — `member_role_id` must be added to `shared_params` first
- Issue 7 (API entity) — entity must expose `member_role_id` in responses
- Issues 3 & 4 (model) — underlying model must accept and validate `member_role_id`
## Labels
issue