From 02a1d95fb2503106443feaea82a20ac472c40ecf Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Tue, 10 Feb 2015 04:22:39 +0100 Subject: Implemented api for project jobs with working tests and updated doc --- CHANGELOG | 1 + doc/api/projects.md | 36 ++++++++++ lib/api/entities.rb | 4 ++ lib/api/projects.rb | 63 +++++++++++++++- spec/requests/api/projects_spec.rb | 143 +++++++++++++++++++++++++++++++++---- 5 files changed, 231 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fbcff57..c17fb74 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ v7.8.0 - Fix OAuth login with GitLab installed in relative URL - GitLab CI has same version as GitLab since now - Allow to pass description and tag list during Runner's registration + - Added api for project jobs v5.4.2 - Fix exposure of project token via build data diff --git a/doc/api/projects.md b/doc/api/projects.md index 1a74b5c..14e84ee 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -150,3 +150,39 @@ Parameters: * `id` (required) - The ID of the Gitlab CI project * `runner_id` (required) - The ID of the Gitlab CI runner +### List All Jobs for a Project + +List the jobs associated to a Gitlab CI Project (only via +authorized user). + + GET /projects/:id/jobs + +Parameters: + + * `id` (required) - The ID of the Gitlab CI project + +### Add a Job to a Project + +Adds a Job to a Gitlab CI Project (only via +authorized user). + + POST /projects/:id/jobs + +Parameters: + + * `id` (required) - The ID of the Gitlab CI project + * `name` (required) - The name of the Job to add + * `commands` (required) - The script commands of the job + +### Remove a Job from a Project + +Removes a Job from a Gitlab CI Project (only +via authorized user). + + DELETE /projects/:id/jobs/:job_id + +Parameters: + + * `id` (required) - The ID of the Gitlab CI project + * `job_id` (required) - The ID of the Job + diff --git a/lib/api/entities.rb b/lib/api/entities.rb index dfd419a..bc21e22 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -27,5 +27,9 @@ module API class WebHook < Grape::Entity expose :id, :project_id, :url end + + class Job < Grape::Entity + expose :id, :name, :commands + end end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index a3cc9f3..7d94c89 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -20,7 +20,7 @@ module API web_hook = project.web_hooks.new({url: params[:web_hook]}) if web_hook.save - present web_hook, :with => Entities::WebHook + present web_hook, with: Entities::WebHook else errors = web_hook.errors.full_messages.join(", ") render_api_error!(errors, 400) @@ -28,6 +28,67 @@ module API end end + # Retrieve all jobs for a project + # + # Parameters + # id (required) - The ID of a project + # Example Request + # GET /projects/:id/jobs + get ":id/jobs" do + project = Project.find(params[:id]) + + not_found! if project.blank? + unauthorized! unless current_user.can_access_project?(project.gitlab_id) + + # present job, with: Entities::Job + project.jobs + end + + # Add a new job to a project + # + # Parameters + # id (required) - The ID of a project + # name (required) - The job name + # commands (required) - The command line script for the job + # Example Request + # POST /projects/:id/jobs + post ":id/jobs" do + required_attributes! [:name, :commands] + + project = Project.find(params[:id]) + + not_found! if project.blank? + unauthorized! unless current_user.can_access_project?(project.gitlab_id) + + job_params = { name: params[:name], commands: params[:commands] } + job = project.jobs.new(job_params) + if job.save + present job, with: Entities::Job + else + errors = job.errors.full_messages.join(", ") + render_api_error!(errors, 400) + end + end + + # Delete a job for a project + # + # Parameters + # id (required) - The ID of a project + # job_id (required) - The ID of the job to delete + # Example Request + # DELETE /projects/:id/jobs/:job_id + delete ":id/jobs/:job_id" do + required_attributes! [:job_id] + + project = Project.find(params[:id]) + job = project.jobs.find(params[:job_id]) + + not_found! if project.blank? || job.blank? + unauthorized! unless current_user.can_access_project?(project.gitlab_id) + + job.destroy + end + # Retrieve all Gitlab CI projects that the user has access to # # Example Request: diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 1209855..7256dfe 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -6,16 +6,16 @@ describe API::API do let(:gitlab_url) { GitlabCi.config.gitlab_server.url } let(:auth_opts) { { - :email => "test@test.com", - :password => "123456" + email: "test@test.com", + password: "123456" } } let(:private_token) { Network.new.authenticate(gitlab_url, auth_opts)["private_token"] } let(:options) { { - :private_token => private_token, - :url => gitlab_url + private_token: private_token, + url: gitlab_url } } @@ -26,8 +26,8 @@ describe API::API do context "requests for scoped projects" do # NOTE: These ids are tied to the actual projects on demo.gitlab.com describe "GET /projects" do - let!(:project1) { FactoryGirl.create(:project, :name => "gitlabhq", :gitlab_id => 3) } - let!(:project2) { FactoryGirl.create(:project, :name => "gitlab-ci", :gitlab_id => 4) } + let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:project, name: "gitlab-ci", gitlab_id: 4) } it "should return all projects on the CI instance" do get api("/projects"), options @@ -40,8 +40,8 @@ describe API::API do describe "GET /projects/owned" do # NOTE: This user doesn't own any of these projects on demo.gitlab.com - let!(:project1) { FactoryGirl.create(:project, :name => "gitlabhq", :gitlab_id => 3) } - let!(:project2) { FactoryGirl.create(:project, :name => "random-project", :gitlab_id => 9898) } + let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:project, name: "random-project", gitlab_id: 9898) } it "should return all projects on the CI instance" do get api("/projects/owned"), options @@ -52,11 +52,124 @@ describe API::API do end end + describe "POST /projects/:project_id/jobs" do + let!(:project) { FactoryGirl.create(:project) } + + let(:job_info) { + { + name: "A Job Name", + commands: "ls -lad", + } + } + let(:invalid_job_info) { {} } + + context "Invalid Job Info" do + before do + options.merge!(invalid_job_info) + end + + it "should error with invalid data" do + post api("/projects/#{project.id}/jobs"), options + response.status.should == 400 + end + end + + context "Valid Job Info" do + before do + options.merge!(job_info) + end + + it "should create a job for specified project" do + post api("/projects/#{project.id}/jobs"), options + response.status.should == 201 + json_response["name"].should == job_info[:name] + json_response["commands"].should == job_info[:commands] + end + + it "fails to create job for non existsing project" do + post api("/projects/non-existant-id/jobs"), options + response.status.should == 404 + end + end + end + + describe "GET /projects/:project_id/jobs" do + let!(:project) { FactoryGirl.create(:project) } + let(:job_info) { + { + name: "A Job Name", + commands: "ls -lad", + } + } + + before do + options.merge!(job_info) + end + + it "should list the project's jobs" do + get api("/projects/#{project.id}/jobs"), options + response.status.should == 200 + json_response.count.should == 1 + json_response.first["project_id"].should == project.id + end + + it "should add & list the project's new jobs" do + post api("/projects/#{project.id}/jobs"), options + response.status.should == 201 + get api("/projects/#{project.id}/jobs"), options + response.status.should == 200 + json_response.count.should == 2 + json_response.first["project_id"].should == project.id + json_response.first["name"].should == job_info[:name] + json_response.first["commands"].should == job_info[:commands] + end + + it "fails to list jobs for non existsing project" do + get api("/projects/non-existant-id/jobs"), options + response.status.should == 404 + end + end + + describe "DELETE /projects/:id/jobs/:job_id" do + let!(:project) { FactoryGirl.create(:project) } + + let(:job_info) { + { + name: "A Job Name", + commands: "ls -lad", + } + } + + before do + options.merge!(job_info) + end + + it "should delete a project job" do + post api("/projects/#{project.id}/jobs"), options + response.status.should == 201 + json_response["name"].should == job_info[:name] + json_response["commands"].should == job_info[:commands] + job_id = json_response["id"] + delete api("/projects/#{project.id}/jobs/#{job_id}"), options + response.status.should == 200 + end + + it "fails to delete a job for a non existsing project" do + delete api("/projects/non-existant-id/jobs/non-existant-job-id"), options + response.status.should == 404 + end + + it "fails to delete a job for a non existsing job id" do + delete api("/projects/#{project.id}/jobs/non-existant-job-id"), options + response.status.should == 404 + end + end + describe "POST /projects/:project_id/webhooks" do let!(:project) { FactoryGirl.create(:project) } context "Valid Webhook URL" do - let!(:webhook) { {:web_hook => "http://example.com/sth/1/ala_ma_kota" } } + let!(:webhook) { {web_hook: "http://example.com/sth/1/ala_ma_kota" } } before do options.merge!(webhook) @@ -76,7 +189,7 @@ describe API::API do end context "Invalid Webhook URL" do - let!(:webhook) { {:web_hook => "ala_ma_kota" } } + let!(:webhook) { {web_hook: "ala_ma_kota" } } before do options.merge!(webhook) @@ -117,7 +230,7 @@ describe API::API do describe "PUT /projects/:id" do let!(:project) { FactoryGirl.create(:project) } - let!(:project_info) { {:name => "An updated name!" } } + let!(:project_info) { {name: "An updated name!" } } before do options.merge!(project_info) @@ -149,10 +262,10 @@ describe API::API do describe "POST /projects" do let(:project_info) { { - :name => "My project", - :gitlab_id => 1, - :gitlab_url => "http://example.com/testing/testing", - :ssh_url_to_repo => "ssh://example.com/testing/testing.git" + name: "My project", + gitlab_id: 1, + gitlab_url: "http://example.com/testing/testing", + ssh_url_to_repo: "ssh://example.com/testing/testing.git" } } -- cgit v1.2.1 From 76256521e42df08cb7ecbd22bcde55c11a97290f Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Thu, 12 Feb 2015 09:56:55 +0100 Subject: project jobs api: exposed active, build_branches, build_tags, tags. Implemented tests and updated documentation --- doc/api/projects.md | 4 ++++ lib/api/entities.rb | 2 +- lib/api/projects.rb | 17 +++++++++++++++-- spec/requests/api/projects_spec.rb | 11 ++++++++++- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 14e84ee..436dcda 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -173,6 +173,10 @@ Parameters: * `id` (required) - The ID of the Gitlab CI project * `name` (required) - The name of the Job to add * `commands` (required) - The script commands of the job + * `active` (optional) - The command is active of not + * `build_branches` (optional) - Trigger commit builds + * `build_tags` (optional) - Trigger tag builds + * `tags` (optional) - The tags associated with this job ### Remove a Job from a Project diff --git a/lib/api/entities.rb b/lib/api/entities.rb index bc21e22..8c4b3c1 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -29,7 +29,7 @@ module API end class Job < Grape::Entity - expose :id, :name, :commands + expose :id, :project_id, :commands, :active, :name, :build_branches, :build_tags, :tags end end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 7d94c89..1226a02 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -40,7 +40,6 @@ module API not_found! if project.blank? unauthorized! unless current_user.can_access_project?(project.gitlab_id) - # present job, with: Entities::Job project.jobs end @@ -50,6 +49,10 @@ module API # id (required) - The ID of a project # name (required) - The job name # commands (required) - The command line script for the job + # active (optional) - The command is active of not + # build_branches (optional) - Trigger commit builds + # build_tags (optional) - Trigger tag builds + # tags (optional) - The tags associated with this job # Example Request # POST /projects/:id/jobs post ":id/jobs" do @@ -60,7 +63,17 @@ module API not_found! if project.blank? unauthorized! unless current_user.can_access_project?(project.gitlab_id) - job_params = { name: params[:name], commands: params[:commands] } + job_params = + { + name: params[:name], + commands: params[:commands], + } + + job_params[:active] = params[:active] unless params[:active].nil? + job_params[:build_branches] = params[:build_branches] unless params[:build_branches].nil? + job_params[:build_tags] = params[:build_tags] unless params[:build_tags].nil? + job_params[:tag_list] = params[:tags] unless params[:tags].nil? + job = project.jobs.new(job_params) if job.save present job, with: Entities::Job diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 7256dfe..669784e 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -59,6 +59,10 @@ describe API::API do { name: "A Job Name", commands: "ls -lad", + active: false, + build_branches: false, + build_tags: true, + tags: "release, deployment", } } let(:invalid_job_info) { {} } @@ -84,6 +88,11 @@ describe API::API do response.status.should == 201 json_response["name"].should == job_info[:name] json_response["commands"].should == job_info[:commands] + json_response["active"].should == job_info[:active] + json_response["build_branches"].should == job_info[:build_branches] + json_response["build_tags"].should == job_info[:build_tags] + json_response["tags"].first["name"].should == "deployment" + json_response["tags"].last["name"].should == "release" end it "fails to create job for non existsing project" do @@ -135,8 +144,8 @@ describe API::API do let(:job_info) { { - name: "A Job Name", commands: "ls -lad", + name: "A Job Name", } } -- cgit v1.2.1 From 122befc180769809d5f093a052b118a1d75cdd6a Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Tue, 24 Feb 2015 16:34:30 +0100 Subject: Fixed test for the projects jobs api --- spec/requests/api/projects_spec.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 669784e..03d704b 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -91,8 +91,7 @@ describe API::API do json_response["active"].should == job_info[:active] json_response["build_branches"].should == job_info[:build_branches] json_response["build_tags"].should == job_info[:build_tags] - json_response["tags"].first["name"].should == "deployment" - json_response["tags"].last["name"].should == "release" + json_response["tags"].should have(2).items end it "fails to create job for non existsing project" do @@ -122,12 +121,21 @@ describe API::API do json_response.first["project_id"].should == project.id end - it "should add & list the project's new jobs" do + it "should delete default job, add & list the project's new jobs" do + # delete default job + get api("/projects/#{project.id}/jobs"), options + response.status.should == 200 + json_response.count.should == 1 + job_id = json_response.first["id"] + delete api("/projects/#{project.id}/jobs/#{job_id}"), options + # add a new job post api("/projects/#{project.id}/jobs"), options response.status.should == 201 + # get the new job get api("/projects/#{project.id}/jobs"), options + # assert job response.status.should == 200 - json_response.count.should == 2 + json_response.count.should == 1 json_response.first["project_id"].should == project.id json_response.first["name"].should == job_info[:name] json_response.first["commands"].should == job_info[:commands] -- cgit v1.2.1