Commit 979ed6c4 authored by Kerri Miller's avatar Kerri Miller

Merge branch '885-api-for-default-description-template-for-issues-and-merge-requests' into 'master'

Allow updating default description templates for projects via API

See merge request gitlab-org/gitlab!55718
parents 0cd13aad 65bfdbfa
...@@ -1005,6 +1005,20 @@ If the project is a fork, and you provide a valid token to authenticate, the ...@@ -1005,6 +1005,20 @@ If the project is a fork, and you provide a valid token to authenticate, the
} }
``` ```
Users of [GitLab Premium or higher](https://about.gitlab.com/pricing/)
can also see the `issues_template` and `merge_requests_template` parameters:
[introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55718)
in GitLab 13.10.
```json
{
"id": 3,
"issues_template": null,
"merge_requests_template": null,
...
}
```
## Get project users ## Get project users
Get the users list of a project. Get the users list of a project.
...@@ -1303,6 +1317,8 @@ PUT /projects/:id ...@@ -1303,6 +1317,8 @@ PUT /projects/:id
| `visibility` | string | **{dotted-circle}** No | See [project visibility level](#project-visibility-level). | | `visibility` | string | **{dotted-circle}** No | See [project visibility level](#project-visibility-level). |
| `wiki_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. | | `wiki_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `wiki_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable wiki for this project. Use `wiki_access_level` instead. | | `wiki_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable wiki for this project. Use `wiki_access_level` instead. |
| `issues_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Issues. Description is parsed with GitLab Flavored Markdown. |
| `merge_requests_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Merge Requests. Description is parsed with GitLab Flavored Markdown. |
## Fork project ## Fork project
......
---
title: Add support for updating default description templates for issues and merge
requests via API
merge_request: 55718
author:
type: added
...@@ -37,6 +37,8 @@ module EE ...@@ -37,6 +37,8 @@ module EE
expose :compliance_frameworks do |project, _| expose :compliance_frameworks do |project, _|
[project.compliance_framework_setting&.compliance_management_framework&.name].compact [project.compliance_framework_setting&.compliance_management_framework&.name].compact
end end
expose :issues_template, if: ->(project, _) { project.feature_available?(:issuable_default_templates) }
expose :merge_requests_template, if: ->(project, _) { project.feature_available?(:issuable_default_templates) }
end end
end end
end end
......
...@@ -33,6 +33,8 @@ module EE ...@@ -33,6 +33,8 @@ module EE
optional :mirror_overwrites_diverged_branches, type: Grape::API::Boolean, desc: 'Pull mirror overwrites diverged branches' optional :mirror_overwrites_diverged_branches, type: Grape::API::Boolean, desc: 'Pull mirror overwrites diverged branches'
optional :import_url, type: String, desc: 'URL from which the project is imported' optional :import_url, type: String, desc: 'URL from which the project is imported'
optional :fallback_approvals_required, type: Integer, desc: 'Overall approvals required when no rule is present' optional :fallback_approvals_required, type: Integer, desc: 'Overall approvals required when no rule is present'
optional :issues_template, type: String, desc: 'Default description for Issues. Description is parsed with GitLab Flavored Markdown.'
optional :merge_requests_template, type: String, desc: 'Default description for Merge Requests. Description is parsed with GitLab Flavored Markdown.'
end end
end end
...@@ -48,8 +50,10 @@ module EE ...@@ -48,8 +50,10 @@ module EE
super.concat [ super.concat [
:approvals_before_merge, :approvals_before_merge,
:external_authorization_classification_label, :external_authorization_classification_label,
:fallback_approvals_required,
:import_url, :import_url,
:fallback_approvals_required :issues_template,
:merge_requests_template
] ]
end end
end end
......
...@@ -82,6 +82,7 @@ module EE ...@@ -82,6 +82,7 @@ module EE
super super
verify_mirror_attrs!(project, attrs) verify_mirror_attrs!(project, attrs)
verify_issuable_default_templates_attrs!(project, attrs)
end end
def verify_mirror_attrs!(project, attrs) def verify_mirror_attrs!(project, attrs)
...@@ -92,6 +93,13 @@ module EE ...@@ -92,6 +93,13 @@ module EE
end end
end end
def verify_issuable_default_templates_attrs!(project, attrs)
unless project.feature_available?(:issuable_default_templates)
attrs.delete(:issues_template)
attrs.delete(:merge_requests_template)
end
end
def check_audit_events_available!(project) def check_audit_events_available!(project)
forbidden! unless project.feature_available?(:audit_events) forbidden! unless project.feature_available?(:audit_events)
end end
......
...@@ -93,6 +93,8 @@ RSpec.describe API::Projects do ...@@ -93,6 +93,8 @@ RSpec.describe API::Projects do
end end
describe 'GET /projects/:id' do describe 'GET /projects/:id' do
subject { get api("/projects/#{project.id}", user) }
context 'with external authorization' do context 'with external authorization' do
let(:project) do let(:project) do
create(:project, create(:project,
...@@ -209,8 +211,6 @@ RSpec.describe API::Projects do ...@@ -209,8 +211,6 @@ RSpec.describe API::Projects do
end end
context 'project soft-deletion' do context 'project soft-deletion' do
subject { get api("/projects/#{project.id}", user) }
let(:project) do let(:project) do
create(:project, :public, archived: true, marked_for_deletion_at: 1.day.ago, deleting_user: user) create(:project, :public, archived: true, marked_for_deletion_at: 1.day.ago, deleting_user: user)
end end
...@@ -253,6 +253,34 @@ RSpec.describe API::Projects do ...@@ -253,6 +253,34 @@ RSpec.describe API::Projects do
end end
end end
end end
context 'issuable default templates feature is available' do
before do
stub_licensed_features(issuable_default_templates: true)
end
it 'returns issuable default templates' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to have_key 'issues_template'
expect(json_response).to have_key 'merge_requests_template'
end
end
context 'issuable default templates feature not available' do
before do
stub_licensed_features(issuable_default_templates: false)
end
it 'does not return issuable default templates' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to have_key 'issues_template'
expect(json_response).not_to have_key 'merge_requests_template'
end
end
end end
# Assumes the following variables are defined: # Assumes the following variables are defined:
...@@ -780,6 +808,65 @@ RSpec.describe API::Projects do ...@@ -780,6 +808,65 @@ RSpec.describe API::Projects do
describe 'PUT /projects/:id' do describe 'PUT /projects/:id' do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
let(:project_params) { {} }
subject { put api("/projects/#{project.id}", user), params: project_params }
context 'issuable default templates feature is available' do
before do
stub_licensed_features(issuable_default_templates: true)
end
context 'when updating issues_template' do
let(:project_params) { { issues_template: '## New Issue Template' } }
it 'updates the content' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['issues_template']).to eq(project_params[:issues_template])
end
end
context 'when updating merge_requests_template' do
let(:project_params) { { merge_requests_template: '## New Merge Request Template' } }
it 'updates the content' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['merge_requests_template']).to eq(project_params[:merge_requests_template])
end
end
end
context 'issuable default templates feature not available' do
before do
stub_licensed_features(issuable_default_templates: false)
end
context 'when updating issues_template' do
let(:project_params) { { issues_template: '## New Issue Template' } }
it 'does not update the content' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to have_key 'issues_template'
end
end
context 'when updating merge_requests_template' do
let(:project_params) { { merge_requests_template: '## New Merge Request Template' } }
it 'does not update the content' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to have_key 'merge_requests_template'
end
end
end
context 'when updating external classification' do context 'when updating external classification' do
before do before do
...@@ -787,8 +874,10 @@ RSpec.describe API::Projects do ...@@ -787,8 +874,10 @@ RSpec.describe API::Projects do
stub_licensed_features(external_authorization_service_api_management: true) stub_licensed_features(external_authorization_service_api_management: true)
end end
let(:project_params) { { external_authorization_classification_label: 'new label' } }
it 'updates the classification label' do it 'updates the classification label' do
put(api("/projects/#{project.id}", user), params: { external_authorization_classification_label: 'new label' }) subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.external_authorization_classification_label).to eq('new label') expect(project.reload.external_authorization_classification_label).to eq('new label')
...@@ -797,7 +886,7 @@ RSpec.describe API::Projects do ...@@ -797,7 +886,7 @@ RSpec.describe API::Projects do
context 'when updating mirror related attributes' do context 'when updating mirror related attributes' do
let(:import_url) { generate(:url) } let(:import_url) { generate(:url) }
let(:mirror_params) do let(:project_params) do
{ {
mirror: true, mirror: true,
import_url: import_url, import_url: import_url,
...@@ -813,7 +902,7 @@ RSpec.describe API::Projects do ...@@ -813,7 +902,7 @@ RSpec.describe API::Projects do
end end
it 'does not update mirror related attributes' do it 'does not update mirror related attributes' do
put(api("/projects/#{project.id}", user), params: mirror_params) subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.mirror).to be false expect(project.reload.mirror).to be false
...@@ -823,12 +912,12 @@ RSpec.describe API::Projects do ...@@ -823,12 +912,12 @@ RSpec.describe API::Projects do
admin = create(:admin) admin = create(:admin)
unrelated_user = create(:user) unrelated_user = create(:user)
mirror_params[:mirror_user_id] = unrelated_user.id project_params[:mirror_user_id] = unrelated_user.id
project.add_maintainer(admin) project.add_maintainer(admin)
expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once
put(api("/projects/#{project.id}", admin), params: mirror_params) put(api("/projects/#{project.id}", admin), params: project_params)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(project.reload).to have_attributes( expect(project.reload).to have_attributes(
...@@ -845,7 +934,7 @@ RSpec.describe API::Projects do ...@@ -845,7 +934,7 @@ RSpec.describe API::Projects do
it 'updates mirror related attributes' do it 'updates mirror related attributes' do
expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once expect_any_instance_of(EE::ProjectImportState).to receive(:force_import_job!).once
put(api("/projects/#{project.id}", user), params: mirror_params) subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(project.reload).to have_attributes( expect(project.reload).to have_attributes(
...@@ -861,7 +950,7 @@ RSpec.describe API::Projects do ...@@ -861,7 +950,7 @@ RSpec.describe API::Projects do
it 'updates project without mirror attributes when the project is unable to set up repository mirroring' do it 'updates project without mirror attributes when the project is unable to set up repository mirroring' do
stub_licensed_features(repository_mirrors: false) stub_licensed_features(repository_mirrors: false)
put(api("/projects/#{project.id}", user), params: mirror_params) subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.mirror).to be false expect(project.reload.mirror).to be false
...@@ -870,9 +959,9 @@ RSpec.describe API::Projects do ...@@ -870,9 +959,9 @@ RSpec.describe API::Projects do
it 'renders an API error when mirror user is invalid' do it 'renders an API error when mirror user is invalid' do
invalid_mirror_user = create(:user) invalid_mirror_user = create(:user)
project.add_developer(invalid_mirror_user) project.add_developer(invalid_mirror_user)
mirror_params[:mirror_user_id] = invalid_mirror_user.id project_params[:mirror_user_id] = invalid_mirror_user.id
put(api("/projects/#{project.id}", user), params: mirror_params) subject
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["message"]["mirror_user_id"].first).to eq("is invalid") expect(json_response["message"]["mirror_user_id"].first).to eq("is invalid")
...@@ -882,7 +971,7 @@ RSpec.describe API::Projects do ...@@ -882,7 +971,7 @@ RSpec.describe API::Projects do
developer = create(:user) developer = create(:user)
project.add_developer(developer) project.add_developer(developer)
put(api("/projects/#{project.id}", developer), params: mirror_params) put(api("/projects/#{project.id}", developer), params: project_params)
expect(response).to have_gitlab_http_status(:forbidden) expect(response).to have_gitlab_http_status(:forbidden)
end end
...@@ -890,10 +979,10 @@ RSpec.describe API::Projects do ...@@ -890,10 +979,10 @@ RSpec.describe API::Projects do
describe 'updating approvals_before_merge attribute' do describe 'updating approvals_before_merge attribute' do
context 'when authenticated as project owner' do context 'when authenticated as project owner' do
it 'updates approvals_before_merge' do let(:project_params) { { approvals_before_merge: 3 } }
project_param = { approvals_before_merge: 3 }
put api("/projects/#{project.id}", user), params: project_param it 'updates approvals_before_merge' do
subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['approvals_before_merge']).to eq(3) expect(json_response['approvals_before_merge']).to eq(3)
......
...@@ -91,6 +91,8 @@ module API ...@@ -91,6 +91,8 @@ module API
end end
params :optional_update_params_ee do params :optional_update_params_ee do
optional :issues_template, type: String, desc: 'Default description for Issues'
optional :merge_requests_template, type: String, desc: 'Default description for Merge Requests'
end end
params :optional_update_params do params :optional_update_params do
...@@ -128,8 +130,10 @@ module API ...@@ -128,8 +130,10 @@ module API
:emails_disabled, :emails_disabled,
:forking_access_level, :forking_access_level,
:issues_access_level, :issues_access_level,
:issues_template,
:lfs_enabled, :lfs_enabled,
:merge_requests_access_level, :merge_requests_access_level,
:merge_requests_template,
:merge_method, :merge_method,
:name, :name,
:only_allow_merge_if_all_discussions_are_resolved, :only_allow_merge_if_all_discussions_are_resolved,
......
...@@ -12,7 +12,6 @@ itself: # project ...@@ -12,7 +12,6 @@ itself: # project
- import_source - import_source
- import_type - import_type
- import_url - import_url
- issues_template
- jobs_cache_index - jobs_cache_index
- last_repository_check_at - last_repository_check_at
- last_repository_check_failed - last_repository_check_failed
...@@ -24,7 +23,6 @@ itself: # project ...@@ -24,7 +23,6 @@ itself: # project
- merge_requests_author_approval - merge_requests_author_approval
- merge_requests_disable_committers_approval - merge_requests_disable_committers_approval
- merge_requests_rebase_enabled - merge_requests_rebase_enabled
- merge_requests_template
- mirror_last_successful_update_at - mirror_last_successful_update_at
- mirror_last_update_at - mirror_last_update_at
- mirror_overwrites_diverged_branches - mirror_overwrites_diverged_branches
......
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