Commit 6f555990 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'rc/fix-tags-api' into 'master'

Fix the /projects/:id/repository/tags endpoint to handle dots in the tag name…

See merge request !13368
parents feb8974c 10fe62ec
--- ---
title: Fix the /projects/:id/repository/branches endpoint to handle dots in the branch title: Fix the /projects/:id/repository/branches endpoint to handle dots in the branch
name when the project full patch contains a `/` name when the project full path contains a `/`
merge_request: 13115 merge_request: 13115
author: author:
---
title: Fix the /projects/:id/repository/tags endpoint to handle dots in the tag name
when the project full path contains a `/`
merge_request: 13368
author:
...@@ -18,17 +18,20 @@ Parameters: ...@@ -18,17 +18,20 @@ Parameters:
[ [
{ {
"commit": { "commit": {
"id": "2695effb5807a22ff3d138d593fd856244e155e7",
"short_id": "2695effb",
"title": "Initial commit",
"created_at": "2017-07-26T11:08:53.000+02:00",
"parent_ids": [
"2a4b78934375d7f53875269ffd4f45fd83a84ebe"
],
"message": "Initial commit",
"author_name": "John Smith", "author_name": "John Smith",
"author_email": "john@example.com", "author_email": "john@example.com",
"authored_date": "2012-05-28T04:42:42-07:00", "authored_date": "2012-05-28T04:42:42-07:00",
"committed_date": "2012-05-28T04:42:42-07:00",
"committer_name": "Jack Smith", "committer_name": "Jack Smith",
"committer_email": "jack@example.com", "committer_email": "jack@example.com",
"id": "2695effb5807a22ff3d138d593fd856244e155e7", "committed_date": "2012-05-28T04:42:42-07:00"
"message": "Initial commit",
"parent_ids": [
"2a4b78934375d7f53875269ffd4f45fd83a84ebe"
]
}, },
"release": { "release": {
"tag_name": "1.0.0", "tag_name": "1.0.0",
...@@ -68,16 +71,19 @@ Example Response: ...@@ -68,16 +71,19 @@ Example Response:
"message": null, "message": null,
"commit": { "commit": {
"id": "60a8ff033665e1207714d6670fcd7b65304ec02f", "id": "60a8ff033665e1207714d6670fcd7b65304ec02f",
"message": "v5.0.0\n", "short_id": "60a8ff03",
"title": "Initial commit",
"created_at": "2017-07-26T11:08:53.000+02:00",
"parent_ids": [ "parent_ids": [
"f61c062ff8bcbdb00e0a1b3317a91aed6ceee06b" "f61c062ff8bcbdb00e0a1b3317a91aed6ceee06b"
], ],
"authored_date": "2015-02-01T21:56:31.000+01:00", "message": "v5.0.0\n",
"author_name": "Arthur Verschaeve", "author_name": "Arthur Verschaeve",
"author_email": "contact@arthurverschaeve.be", "author_email": "contact@arthurverschaeve.be",
"committed_date": "2015-02-01T21:56:31.000+01:00", "authored_date": "2015-02-01T21:56:31.000+01:00",
"committer_name": "Arthur Verschaeve", "committer_name": "Arthur Verschaeve",
"committer_email": "contact@arthurverschaeve.be" "committer_email": "contact@arthurverschaeve.be",
"committed_date": "2015-02-01T21:56:31.000+01:00"
}, },
"release": null "release": null
} }
...@@ -102,17 +108,20 @@ Parameters: ...@@ -102,17 +108,20 @@ Parameters:
```json ```json
{ {
"commit": { "commit": {
"id": "2695effb5807a22ff3d138d593fd856244e155e7",
"short_id": "2695effb",
"title": "Initial commit",
"created_at": "2017-07-26T11:08:53.000+02:00",
"parent_ids": [
"2a4b78934375d7f53875269ffd4f45fd83a84ebe"
],
"message": "Initial commit",
"author_name": "John Smith", "author_name": "John Smith",
"author_email": "john@example.com", "author_email": "john@example.com",
"authored_date": "2012-05-28T04:42:42-07:00", "authored_date": "2012-05-28T04:42:42-07:00",
"committed_date": "2012-05-28T04:42:42-07:00",
"committer_name": "Jack Smith", "committer_name": "Jack Smith",
"committer_email": "jack@example.com", "committer_email": "jack@example.com",
"id": "2695effb5807a22ff3d138d593fd856244e155e7", "committed_date": "2012-05-28T04:42:42-07:00"
"message": "Initial commit",
"parent_ids": [
"2a4b78934375d7f53875269ffd4f45fd83a84ebe"
]
}, },
"release": { "release": {
"tag_name": "1.0.0", "tag_name": "1.0.0",
......
...@@ -699,7 +699,7 @@ module API ...@@ -699,7 +699,7 @@ module API
class RepoTag < Grape::Entity class RepoTag < Grape::Entity
expose :name, :message expose :name, :message
expose :commit do |repo_tag, options| expose :commit, using: Entities::RepoCommit do |repo_tag, options|
options[:project].repository.commit(repo_tag.dereferenced_target) options[:project].repository.commit(repo_tag.dereferenced_target)
end end
......
...@@ -2,19 +2,21 @@ module API ...@@ -2,19 +2,21 @@ module API
class Tags < Grape::API class Tags < Grape::API
include PaginationParams include PaginationParams
TAG_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
before { authorize! :download_code, user_project } before { authorize! :download_code, user_project }
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a project repository tags' do desc 'Get a project repository tags' do
success Entities::RepoTag success Entities::RepoTag
end end
params do params do
use :pagination use :pagination
end end
get ":id/repository/tags" do get ':id/repository/tags' do
tags = ::Kaminari.paginate_array(user_project.repository.tags.sort_by(&:name).reverse) tags = ::Kaminari.paginate_array(user_project.repository.tags.sort_by(&:name).reverse)
present paginate(tags), with: Entities::RepoTag, project: user_project present paginate(tags), with: Entities::RepoTag, project: user_project
end end
...@@ -25,7 +27,7 @@ module API ...@@ -25,7 +27,7 @@ module API
params do params do
requires :tag_name, type: String, desc: 'The name of the tag' requires :tag_name, type: String, desc: 'The name of the tag'
end end
get ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do get ':id/repository/tags/:tag_name', requirements: TAG_ENDPOINT_REQUIREMENTS do
tag = user_project.repository.find_tag(params[:tag_name]) tag = user_project.repository.find_tag(params[:tag_name])
not_found!('Tag') unless tag not_found!('Tag') unless tag
...@@ -60,7 +62,7 @@ module API ...@@ -60,7 +62,7 @@ module API
params do params do
requires :tag_name, type: String, desc: 'The name of the tag' requires :tag_name, type: String, desc: 'The name of the tag'
end end
delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do delete ':id/repository/tags/:tag_name', requirements: TAG_ENDPOINT_REQUIREMENTS do
authorize_push_project authorize_push_project
result = ::Tags::DestroyService.new(user_project, current_user) result = ::Tags::DestroyService.new(user_project, current_user)
...@@ -78,7 +80,7 @@ module API ...@@ -78,7 +80,7 @@ module API
requires :tag_name, type: String, desc: 'The name of the tag' requires :tag_name, type: String, desc: 'The name of the tag'
requires :description, type: String, desc: 'Release notes with markdown support' requires :description, type: String, desc: 'Release notes with markdown support'
end end
post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.+/ } do post ':id/repository/tags/:tag_name/release', requirements: TAG_ENDPOINT_REQUIREMENTS do
authorize_push_project authorize_push_project
result = CreateReleaseService.new(user_project, current_user) result = CreateReleaseService.new(user_project, current_user)
...@@ -98,7 +100,7 @@ module API ...@@ -98,7 +100,7 @@ module API
requires :tag_name, type: String, desc: 'The name of the tag' requires :tag_name, type: String, desc: 'The name of the tag'
requires :description, type: String, desc: 'Release notes with markdown support' requires :description, type: String, desc: 'Release notes with markdown support'
end end
put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.+/ } do put ':id/repository/tags/:tag_name/release', requirements: TAG_ENDPOINT_REQUIREMENTS do
authorize_push_project authorize_push_project
result = UpdateReleaseService.new(user_project, current_user) result = UpdateReleaseService.new(user_project, current_user)
......
{
"type": "object",
"required" : [
"tag_name",
"description"
],
"properties" : {
"tag_name": { "type": ["string", "null"] },
"description": { "type": "string" }
},
"additionalProperties": false
}
{
"type": "object",
"required" : [
"name",
"message",
"commit",
"release"
],
"properties" : {
"name": { "type": "string" },
"message": { "type": ["string", "null"] },
"commit": { "$ref": "commit/basic.json" },
"release": {
"oneOf": [
{ "type": "null" },
{ "$ref": "release.json" }
]
}
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "tag.json" }
}
require 'spec_helper' require 'spec_helper'
require 'mime/types'
describe API::Tags do describe API::Tags do
include RepoHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let!(:project) { create(:project, :repository, creator: user) } let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let!(:master) { create(:project_member, :master, user: user, project: project) } let(:tag_name) { project.repository.find_tag('v1.1.0').name }
let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
describe "GET /projects/:id/repository/tags" do let(:project_id) { project.id }
let(:tag_name) { project.repository.tag_names.sort.reverse.first } let(:current_user) { nil }
let(:description) { 'Awesome release!' }
before do
project.add_master(user)
end
describe 'GET /projects/:id/repository/tags' do
let(:route) { "/projects/#{project_id}/repository/tags" }
shared_examples_for 'repository tags' do shared_examples_for 'repository tags' do
it 'returns the repository tags' do it 'returns the repository tags' do
get api("/projects/#{project.id}/repository/tags", current_user) get api(route, current_user)
expect(response).to have_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/tags')
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name) expect(json_response.first['name']).to eq(tag_name)
end end
end
context 'when unauthenticated' do context 'when repository is disabled' do
it_behaves_like 'repository tags' do include_context 'disabled repository'
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil } it_behaves_like '403 response' do
let(:request) { get api(route, current_user) }
end
end end
end end
context 'when authenticated' do context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository tags' do let(:project) { create(:project, :public, :repository) }
let(:current_user) { user }
it_behaves_like 'repository tags'
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route) }
let(:message) { '404 Project Not Found' }
end end
end end
context 'without releases' do context 'when authenticated', 'as a master' do
it "returns an array of project tags" do let(:current_user) { user }
get api("/projects/#{project.id}/repository/tags", user)
expect(response).to have_http_status(200) it_behaves_like 'repository tags'
expect(response).to include_pagination_headers
expect(json_response).to be_an Array context 'requesting with the escaped project full path' do
expect(json_response.first['name']).to eq(tag_name) let(:project_id) { CGI.escape(project.full_path) }
it_behaves_like 'repository tags'
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
end end
end end
context 'with releases' do context 'with releases' do
let(:description) { 'Awesome release!' }
before do before do
release = project.releases.find_or_initialize_by(tag: tag_name) release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: description) release.update_attributes(description: description)
end end
it "returns an array of project tags with release info" do it 'returns an array of project tags with release info' do
get api("/projects/#{project.id}/repository/tags", user) get api(route, user)
expect(response).to have_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/tags')
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name) expect(json_response.first['name']).to eq(tag_name)
expect(json_response.first['message']).to eq('Version 1.1.0') expect(json_response.first['message']).to eq('Version 1.1.0')
expect(json_response.first['release']['description']).to eq(description) expect(json_response.first['release']['description']).to eq(description)
...@@ -69,210 +88,342 @@ describe API::Tags do ...@@ -69,210 +88,342 @@ describe API::Tags do
end end
describe 'GET /projects/:id/repository/tags/:tag_name' do describe 'GET /projects/:id/repository/tags/:tag_name' do
let(:tag_name) { project.repository.tag_names.sort.reverse.first } let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}" }
shared_examples_for 'repository tag' do shared_examples_for 'repository tag' do
it 'returns the repository tag' do it 'returns the repository branch' do
get api("/projects/#{project.id}/repository/tags/#{tag_name}", current_user) get api(route, current_user)
expect(response).to have_http_status(200)
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/tag')
expect(json_response['name']).to eq(tag_name) expect(json_response['name']).to eq(tag_name)
end end
it 'returns 404 for an invalid tag name' do context 'when tag does not exist' do
get api("/projects/#{project.id}/repository/tags/foobar", current_user) let(:tag_name) { 'unknown' }
expect(response).to have_http_status(404) it_behaves_like '404 response' do
let(:request) { get api(route, current_user) }
let(:message) { '404 Tag Not Found' }
end
end
context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do
let(:request) { get api(route, current_user) }
end
end end
end end
context 'when unauthenticated' do context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository tag' do let(:project) { create(:project, :public, :repository) }
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil } it_behaves_like 'repository tag'
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route) }
let(:message) { '404 Project Not Found' }
end end
end end
context 'when authenticated' do context 'when authenticated', 'as a master' do
it_behaves_like 'repository tag' do let(:current_user) { user }
let(:current_user) { user }
it_behaves_like 'repository tag'
context 'requesting with the escaped project full path' do
let(:project_id) { CGI.escape(project.full_path) }
it_behaves_like 'repository tag'
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
end end
end end
end end
describe 'POST /projects/:id/repository/tags' do describe 'POST /projects/:id/repository/tags' do
context 'lightweight tags' do let(:tag_name) { 'new_tag' }
let(:route) { "/projects/#{project_id}/repository/tags" }
shared_examples_for 'repository new tag' do
it 'creates a new tag' do it 'creates a new tag' do
post api("/projects/#{project.id}/repository/tags", user), post api(route, current_user), tag_name: tag_name, ref: 'master'
tag_name: 'v7.0.1',
ref: 'master'
expect(response).to have_http_status(201) expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq('v7.0.1') expect(response).to match_response_schema('public_api/v4/tag')
expect(json_response['name']).to eq(tag_name)
end end
end
context 'lightweight tags with release notes' do context 'when repository is disabled' do
it 'creates a new tag' do include_context 'disabled repository'
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v7.0.1',
ref: 'master',
release_description: 'Wow'
expect(response).to have_http_status(201) it_behaves_like '403 response' do
expect(json_response['name']).to eq('v7.0.1') let(:request) { post api(route, current_user) }
expect(json_response['release']['description']).to eq('Wow') end
end end
end end
describe 'DELETE /projects/:id/repository/tags/:tag_name' do context 'when unauthenticated', 'and project is private' do
let(:tag_name) { project.repository.tag_names.sort.reverse.first } it_behaves_like '404 response' do
let(:request) { post api(route) }
let(:message) { '404 Project Not Found' }
end
end
before do context 'when authenticated', 'as a guest' do
allow_any_instance_of(Repository).to receive(:rm_tag).and_return(true) it_behaves_like '403 response' do
let(:request) { post api(route, guest) }
end end
end
context 'when authenticated', 'as a master' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
it_behaves_like 'repository new tag'
context 'delete tag' do context 'when tag contains a dot' do
it 'deletes an existing tag' do let(:tag_name) { 'v7.0.1' }
delete api("/projects/#{project.id}/repository/tags/#{tag_name}", user)
expect(response).to have_http_status(204) it_behaves_like 'repository new tag'
end end
it 'raises 404 if the tag does not exist' do context 'requesting with the escaped project full path' do
delete api("/projects/#{project.id}/repository/tags/foobar", user) let(:project_id) { CGI.escape(project.full_path) }
expect(response).to have_http_status(404)
it_behaves_like 'repository new tag'
context 'when tag contains a dot' do
let(:tag_name) { 'v7.0.1' }
it_behaves_like 'repository new tag'
end
end end
end end
end
context 'annotated tag' do it 'returns 400 if tag name is invalid' do
it 'creates a new annotated tag' do post api(route, current_user), tag_name: 'new design', ref: 'master'
# Identity must be set in .gitconfig to create annotated tag.
repo_path = project.repository.path_to_repo expect(response).to have_gitlab_http_status(400)
system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name})) expect(json_response['message']).to eq('Tag name invalid')
system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email})) end
it 'returns 400 if tag already exists' do
post api(route, current_user), tag_name: 'new_design1', ref: 'master'
post api("/projects/#{project.id}/repository/tags", user), expect(response).to have_gitlab_http_status(201)
tag_name: 'v7.1.0', expect(response).to match_response_schema('public_api/v4/tag')
ref: 'master',
message: 'Release 7.1.0'
expect(response).to have_http_status(201) post api(route, current_user), tag_name: 'new_design1', ref: 'master'
expect(json_response['name']).to eq('v7.1.0')
expect(json_response['message']).to eq('Release 7.1.0') expect(response).to have_gitlab_http_status(400)
expect(json_response['message']).to eq('Tag new_design1 already exists')
end end
end
it 'denies for user without push access' do it 'returns 400 if ref name is invalid' do
post api("/projects/#{project.id}/repository/tags", user2), post api(route, current_user), tag_name: 'new_design3', ref: 'foo'
tag_name: 'v1.9.0',
ref: '621491c677087aa243f165eab467bfdfbee00be1' expect(response).to have_gitlab_http_status(400)
expect(response).to have_http_status(403) expect(json_response['message']).to eq('Target foo is invalid')
end
context 'lightweight tags with release notes' do
it 'creates a new tag' do
post api(route, current_user), tag_name: tag_name, ref: 'master', release_description: 'Wow'
expect(response).to have_gitlab_http_status(201)
expect(response).to match_response_schema('public_api/v4/tag')
expect(json_response['name']).to eq(tag_name)
expect(json_response['release']['description']).to eq('Wow')
end
end
context 'annotated tag' do
it 'creates a new annotated tag' do
# Identity must be set in .gitconfig to create annotated tag.
repo_path = project.repository.path_to_repo
system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
post api(route, current_user), tag_name: 'v7.1.0', ref: 'master', message: 'Release 7.1.0'
expect(response).to have_gitlab_http_status(201)
expect(response).to match_response_schema('public_api/v4/tag')
expect(json_response['name']).to eq('v7.1.0')
expect(json_response['message']).to eq('Release 7.1.0')
end
end
end end
end
describe 'DELETE /projects/:id/repository/tags/:tag_name' do
let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}" }
it 'returns 400 if tag name is invalid' do before do
post api("/projects/#{project.id}/repository/tags", user), allow_any_instance_of(Repository).to receive(:rm_tag).and_return(true)
tag_name: 'v 1.0.0',
ref: 'master'
expect(response).to have_http_status(400)
expect(json_response['message']).to eq('Tag name invalid')
end end
it 'returns 400 if tag already exists' do shared_examples_for 'repository delete tag' do
post api("/projects/#{project.id}/repository/tags", user), it 'deletes a tag' do
tag_name: 'v8.0.0', delete api(route, current_user)
ref: 'master'
expect(response).to have_http_status(201) expect(response).to have_gitlab_http_status(204)
post api("/projects/#{project.id}/repository/tags", user), end
tag_name: 'v8.0.0',
ref: 'master' context 'when tag does not exist' do
expect(response).to have_http_status(400) let(:tag_name) { 'unknown' }
expect(json_response['message']).to eq('Tag v8.0.0 already exists')
it_behaves_like '404 response' do
let(:request) { delete api(route, current_user) }
let(:message) { 'No such tag' }
end
end
context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do
let(:request) { delete api(route, current_user) }
end
end
end end
it 'returns 400 if ref name is invalid' do context 'when authenticated', 'as a master' do
post api("/projects/#{project.id}/repository/tags", user), let(:current_user) { user }
tag_name: 'mytag',
ref: 'foo' it_behaves_like 'repository delete tag'
expect(response).to have_http_status(400)
expect(json_response['message']).to eq('Target foo is invalid') context 'requesting with the escaped project full path' do
let(:project_id) { CGI.escape(project.full_path) }
it_behaves_like 'repository delete tag'
end
end end
end end
describe 'POST /projects/:id/repository/tags/:tag_name/release' do describe 'POST /projects/:id/repository/tags/:tag_name/release' do
let(:tag_name) { project.repository.tag_names.first } let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/release" }
let(:description) { 'Awesome release!' } let(:description) { 'Awesome release!' }
it 'creates description for existing git tag' do shared_examples_for 'repository new release' do
post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), it 'creates description for existing git tag' do
description: description post api(route, user), description: description
expect(response).to have_http_status(201) expect(response).to have_gitlab_http_status(201)
expect(json_response['tag_name']).to eq(tag_name) expect(response).to match_response_schema('public_api/v4/release')
expect(json_response['description']).to eq(description) expect(json_response['tag_name']).to eq(tag_name)
end expect(json_response['description']).to eq(description)
end
context 'when tag does not exist' do
let(:tag_name) { 'unknown' }
it_behaves_like '404 response' do
let(:request) { post api(route, current_user), description: description }
let(:message) { 'Tag does not exist' }
end
end
it 'returns 404 if the tag does not exist' do context 'when repository is disabled' do
post api("/projects/#{project.id}/repository/tags/foobar/release", user), include_context 'disabled repository'
description: description
expect(response).to have_http_status(404) it_behaves_like '403 response' do
expect(json_response['message']).to eq('Tag does not exist') let(:request) { post api(route, current_user), description: description }
end
end
end end
context 'on tag with existing release' do context 'when authenticated', 'as a master' do
before do let(:current_user) { user }
release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: description) it_behaves_like 'repository new release'
context 'requesting with the escaped project full path' do
let(:project_id) { CGI.escape(project.full_path) }
it_behaves_like 'repository new release'
end end
it 'returns 409 if there is already a release' do context 'on tag with existing release' do
post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), before do
description: description release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: description)
end
it 'returns 409 if there is already a release' do
post api(route, user), description: description
expect(response).to have_http_status(409) expect(response).to have_gitlab_http_status(409)
expect(json_response['message']).to eq('Release already exists') expect(json_response['message']).to eq('Release already exists')
end
end end
end end
end end
describe 'PUT id/repository/tags/:tag_name/release' do describe 'PUT id/repository/tags/:tag_name/release' do
let(:tag_name) { project.repository.tag_names.first } let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/release" }
let(:description) { 'Awesome release!' } let(:description) { 'Awesome release!' }
let(:new_description) { 'The best release!' } let(:new_description) { 'The best release!' }
context 'on tag with existing release' do shared_examples_for 'repository update release' do
before do context 'on tag with existing release' do
release = project.releases.find_or_initialize_by(tag: tag_name) before do
release.update_attributes(description: description) release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: description)
end
it 'updates the release description' do
put api(route, current_user), description: new_description
expect(response).to have_gitlab_http_status(200)
expect(json_response['tag_name']).to eq(tag_name)
expect(json_response['description']).to eq(new_description)
end
end end
it 'updates the release description' do context 'when tag does not exist' do
put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), let(:tag_name) { 'unknown' }
description: new_description
expect(response).to have_http_status(200) it_behaves_like '404 response' do
expect(json_response['tag_name']).to eq(tag_name) let(:request) { put api(route, current_user), description: new_description }
expect(json_response['description']).to eq(new_description) let(:message) { 'Tag does not exist' }
end
end end
end
it 'returns 404 if the tag does not exist' do context 'when repository is disabled' do
put api("/projects/#{project.id}/repository/tags/foobar/release", user), include_context 'disabled repository'
description: new_description
expect(response).to have_http_status(404) it_behaves_like '403 response' do
expect(json_response['message']).to eq('Tag does not exist') let(:request) { put api(route, current_user), description: new_description }
end
end
end end
it 'returns 404 if the release does not exist' do context 'when authenticated', 'as a master' do
put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), let(:current_user) { user }
description: new_description
it_behaves_like 'repository update release'
expect(response).to have_http_status(404) context 'requesting with the escaped project full path' do
expect(json_response['message']).to eq('Release does not exist') let(:project_id) { CGI.escape(project.full_path) }
it_behaves_like 'repository update release'
end
context 'when release does not exist' do
it_behaves_like '404 response' do
let(:request) { put api(route, current_user), description: new_description }
let(:message) { 'Release does not exist' }
end
end
end end
end end
end end
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