diff options
author | Robert Schilling <rschilling@student.tugraz.at> | 2017-02-06 14:25:49 +0100 |
---|---|---|
committer | Robert Schilling <rschilling@student.tugraz.at> | 2017-02-07 12:16:00 +0100 |
commit | 5985b55769303824f0ce64ae293b0498f2edfc1b (patch) | |
tree | 22a07e06188ce4c22cfe5f03cf23f7684cf59306 | |
parent | 96baf2bc4f0f6177badd61e59f5a0434dd12f7ac (diff) | |
download | gitlab-ce-5985b55769303824f0ce64ae293b0498f2edfc1b.tar.gz |
Remove deprecated 'expires_at' from project snippets API
-rw-r--r-- | changelogs/unreleased/api-remove-snippets-expires-at.yml | 4 | ||||
-rw-r--r-- | doc/api/project_snippets.md | 1 | ||||
-rw-r--r-- | doc/api/v3_to_v4.md | 2 | ||||
-rw-r--r-- | lib/api/api.rb | 1 | ||||
-rw-r--r-- | lib/api/entities.rb | 17 | ||||
-rw-r--r-- | lib/api/v3/project_snippets.rb | 135 | ||||
-rw-r--r-- | spec/requests/api/project_snippets_spec.rb | 12 | ||||
-rw-r--r-- | spec/requests/api/v3/project_snippets_spec.rb | 188 |
8 files changed, 343 insertions, 17 deletions
diff --git a/changelogs/unreleased/api-remove-snippets-expires-at.yml b/changelogs/unreleased/api-remove-snippets-expires-at.yml new file mode 100644 index 00000000000..67603bfab3b --- /dev/null +++ b/changelogs/unreleased/api-remove-snippets-expires-at.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Remove deprecated ''expires_at'' from project snippets' +merge_request: 8723 +author: Robert Schilling diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md index c6685f54a9d..404876f6237 100644 --- a/doc/api/project_snippets.md +++ b/doc/api/project_snippets.md @@ -51,7 +51,6 @@ Parameters: "state": "active", "created_at": "2012-05-23T08:00:58Z" }, - "expires_at": null, "updated_at": "2012-06-28T10:52:04Z", "created_at": "2012-06-28T10:52:04Z", "web_url": "http://example.com/example/example/snippets/1" diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 9748aec17ad..8408f8e286e 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -10,4 +10,4 @@ changes are in V4: - `iid` filter has been removed from `projects/:id/issues` - `projects/:id/merge_requests?iid[]=x&iid[]=y` array filter has been renamed to `iids` - Endpoints under `projects/merge_request/:id` have been removed (use: `projects/merge_requests/:id`) - +- Project snippets do not return deprecated field `expires_at` diff --git a/lib/api/api.rb b/lib/api/api.rb index 1950d2791ab..1b008b527bc 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -8,6 +8,7 @@ module API mount ::API::V3::Issues mount ::API::V3::MergeRequests mount ::API::V3::Projects + mount ::API::V3::ProjectSnippets end before { allow_access_with_scope :api } diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b1ead48caf7..d296fbb9313 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -213,9 +213,6 @@ module API expose :author, using: Entities::UserBasic expose :updated_at, :created_at - # TODO (rspeicher): Deprecated; remove in 9.0 - expose(:expires_at) { |snippet| nil } - expose :web_url do |snippet, options| Gitlab::UrlBuilder.build(snippet) end @@ -697,5 +694,19 @@ module API expose :id, :message, :starts_at, :ends_at, :color, :font expose :active?, as: :active end + + # Entities for the deprecated V3 API + class ProjectSnippetV3 < Grape::Entity + expose :id, :title, :file_name + expose :author, using: Entities::UserBasic + expose :updated_at, :created_at + + # TODO (rspeicher): Deprecated; remove in 9.0 + expose(:expires_at) { |snippet| nil } + + expose :web_url do |snippet, options| + Gitlab::UrlBuilder.build(snippet) + end + end end end diff --git a/lib/api/v3/project_snippets.rb b/lib/api/v3/project_snippets.rb new file mode 100644 index 00000000000..920cc92217f --- /dev/null +++ b/lib/api/v3/project_snippets.rb @@ -0,0 +1,135 @@ +module API + module V3 + class ProjectSnippets < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + helpers do + def handle_project_member_errors(errors) + if errors[:project_access].any? + error!(errors[:project_access], 422) + end + not_found! + end + + def snippets_for_current_user + finder_params = { filter: :by_project, project: user_project } + SnippetsFinder.new.execute(current_user, finder_params) + end + end + + desc 'Get all project snippets' do + success Entities::ProjectSnippetV3 + end + params do + use :pagination + end + get ":id/snippets" do + present paginate(snippets_for_current_user), with: Entities::ProjectSnippetV3 + end + + desc 'Get a single project snippet' do + success Entities::ProjectSnippetV3 + end + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + end + get ":id/snippets/:snippet_id" do + snippet = snippets_for_current_user.find(params[:snippet_id]) + present snippet, with: Entities::ProjectSnippetV3 + end + + desc 'Create a new project snippet' do + success Entities::ProjectSnippetV3 + end + params do + requires :title, type: String, desc: 'The title of the snippet' + requires :file_name, type: String, desc: 'The file name of the snippet' + requires :code, type: String, desc: 'The content of the snippet' + requires :visibility_level, type: Integer, + values: [Gitlab::VisibilityLevel::PRIVATE, + Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PUBLIC], + desc: 'The visibility level of the snippet' + end + post ":id/snippets" do + authorize! :create_project_snippet, user_project + snippet_params = declared_params.merge(request: request, api: true) + snippet_params[:content] = snippet_params.delete(:code) + + snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute + + if snippet.persisted? + present snippet, with: Entities::ProjectSnippetV3 + else + render_validation_error!(snippet) + end + end + + desc 'Update an existing project snippet' do + success Entities::ProjectSnippetV3 + end + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + optional :title, type: String, desc: 'The title of the snippet' + optional :file_name, type: String, desc: 'The file name of the snippet' + optional :code, type: String, desc: 'The content of the snippet' + optional :visibility_level, type: Integer, + values: [Gitlab::VisibilityLevel::PRIVATE, + Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PUBLIC], + desc: 'The visibility level of the snippet' + at_least_one_of :title, :file_name, :code, :visibility_level + end + put ":id/snippets/:snippet_id" do + snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id)) + not_found!('Snippet') unless snippet + + authorize! :update_project_snippet, snippet + + snippet_params = declared_params(include_missing: false) + snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present? + + UpdateSnippetService.new(user_project, current_user, snippet, + snippet_params).execute + + if snippet.persisted? + present snippet, with: Entities::ProjectSnippetV3 + else + render_validation_error!(snippet) + end + end + + desc 'Delete a project snippet' + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + end + delete ":id/snippets/:snippet_id" do + snippet = snippets_for_current_user.find_by(id: params[:snippet_id]) + not_found!('Snippet') unless snippet + + authorize! :admin_project_snippet, snippet + snippet.destroy + end + + desc 'Get a raw project snippet' + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + end + get ":id/snippets/:snippet_id/raw" do + snippet = snippets_for_current_user.find_by(id: params[:snippet_id]) + not_found!('Snippet') unless snippet + + env['api.format'] = :txt + content_type 'text/plain' + present snippet.content + end + end + end + end +end diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 45d5ae267c5..eea76c7bb94 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -7,18 +7,6 @@ describe API::ProjectSnippets, api: true do let(:user) { create(:user) } let(:admin) { create(:admin) } - describe 'GET /projects/:project_id/snippets/:id' do - # TODO (rspeicher): Deprecated; remove in 9.0 - it 'always exposes expires_at as nil' do - snippet = create(:project_snippet, author: admin) - - get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin) - - expect(json_response).to have_key('expires_at') - expect(json_response['expires_at']).to be_nil - end - end - describe 'GET /projects/:project_id/snippets/' do let(:user) { create(:user) } diff --git a/spec/requests/api/v3/project_snippets_spec.rb b/spec/requests/api/v3/project_snippets_spec.rb new file mode 100644 index 00000000000..3700477f0db --- /dev/null +++ b/spec/requests/api/v3/project_snippets_spec.rb @@ -0,0 +1,188 @@ +require 'rails_helper' + +describe API::ProjectSnippets, api: true do + include ApiHelpers + + let(:project) { create(:empty_project, :public) } + let(:user) { create(:user) } + let(:admin) { create(:admin) } + + describe 'GET /projects/:project_id/snippets/:id' do + # TODO (rspeicher): Deprecated; remove in 9.0 + it 'always exposes expires_at as nil' do + snippet = create(:project_snippet, author: admin) + + get v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin) + + expect(json_response).to have_key('expires_at') + expect(json_response['expires_at']).to be_nil + end + end + + describe 'GET /projects/:project_id/snippets/' do + let(:user) { create(:user) } + + it 'returns all snippets available to team member' do + project.add_developer(user) + public_snippet = create(:project_snippet, :public, project: project) + internal_snippet = create(:project_snippet, :internal, project: project) + private_snippet = create(:project_snippet, :private, project: project) + + get v3_api("/projects/#{project.id}/snippets/", user) + + expect(response).to have_http_status(200) + expect(json_response.size).to eq(3) + expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id) + expect(json_response.last).to have_key('web_url') + end + + it 'hides private snippets from regular user' do + create(:project_snippet, :private, project: project) + + get v3_api("/projects/#{project.id}/snippets/", user) + expect(response).to have_http_status(200) + expect(json_response.size).to eq(0) + end + end + + describe 'POST /projects/:project_id/snippets/' do + let(:params) do + { + title: 'Test Title', + file_name: 'test.rb', + code: 'puts "hello world"', + visibility_level: Snippet::PUBLIC + } + end + + it 'creates a new snippet' do + post v3_api("/projects/#{project.id}/snippets/", admin), params + + expect(response).to have_http_status(201) + snippet = ProjectSnippet.find(json_response['id']) + expect(snippet.content).to eq(params[:code]) + expect(snippet.title).to eq(params[:title]) + expect(snippet.file_name).to eq(params[:file_name]) + expect(snippet.visibility_level).to eq(params[:visibility_level]) + end + + it 'returns 400 for missing parameters' do + params.delete(:title) + + post v3_api("/projects/#{project.id}/snippets/", admin), params + + expect(response).to have_http_status(400) + end + + context 'when the snippet is spam' do + def create_snippet(project, snippet_params = {}) + project.add_developer(user) + + post v3_api("/projects/#{project.id}/snippets", user), params.merge(snippet_params) + end + + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end + + context 'when the project is private' do + let(:private_project) { create(:project_empty_repo, :private) } + + context 'when the snippet is public' do + it 'creates the snippet' do + expect { create_snippet(private_project, visibility_level: Snippet::PUBLIC) }. + to change { Snippet.count }.by(1) + end + end + end + + context 'when the project is public' do + context 'when the snippet is private' do + it 'creates the snippet' do + expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. + to change { Snippet.count }.by(1) + end + end + + context 'when the snippet is public' do + it 'rejects the shippet' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + not_to change { Snippet.count } + expect(response).to have_http_status(400) + end + + it 'creates a spam log' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + end + end + end + end + + describe 'PUT /projects/:project_id/snippets/:id/' do + let(:snippet) { create(:project_snippet, author: admin) } + + it 'updates snippet' do + new_content = 'New content' + + put v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content + + expect(response).to have_http_status(200) + snippet.reload + expect(snippet.content).to eq(new_content) + end + + it 'returns 404 for invalid snippet id' do + put v3_api("/projects/#{snippet.project.id}/snippets/1234", admin), title: 'foo' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + + it 'returns 400 for missing parameters' do + put v3_api("/projects/#{project.id}/snippets/1234", admin) + + expect(response).to have_http_status(400) + end + end + + describe 'DELETE /projects/:project_id/snippets/:id/' do + let(:snippet) { create(:project_snippet, author: admin) } + + it 'deletes snippet' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + + delete v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin) + + expect(response).to have_http_status(200) + end + + it 'returns 404 for invalid snippet id' do + delete v3_api("/projects/#{snippet.project.id}/snippets/1234", admin) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + end + + describe 'GET /projects/:project_id/snippets/:id/raw' do + let(:snippet) { create(:project_snippet, author: admin) } + + it 'returns raw text' do + get v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin) + + expect(response).to have_http_status(200) + expect(response.content_type).to eq 'text/plain' + expect(response.body).to eq(snippet.content) + end + + it 'returns 404 for invalid snippet id' do + delete v3_api("/projects/#{snippet.project.id}/snippets/1234", admin) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + end +end |