Commit aa04f84f authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch 'eb-code-coverage-graph-api' into 'master'

Implement endpoints for daily code coverage graph

See merge request gitlab-org/gitlab!32681
parents 57f6abcd e4db1cf8
...@@ -12,7 +12,8 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati ...@@ -12,7 +12,8 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
def index def index
respond_to do |format| respond_to do |format|
format.csv { send_data(render_csv(results), type: 'text/csv; charset=utf-8') } format.csv { send_data(render_csv(report_results), type: 'text/csv; charset=utf-8') }
format.json { render json: render_json(report_results) }
end end
end end
...@@ -37,7 +38,11 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati ...@@ -37,7 +38,11 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
).render ).render
end end
def results def render_json(collection)
Ci::DailyBuildGroupReportResultSerializer.new.represent(collection, param_type: param_type)
end
def report_results
Ci::DailyBuildGroupReportResultsFinder.new(finder_params).execute Ci::DailyBuildGroupReportResultsFinder.new(finder_params).execute
end end
......
...@@ -71,6 +71,11 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -71,6 +71,11 @@ class Projects::GraphsController < Projects::ApplicationController
namespace_id: @project.namespace, namespace_id: @project.namespace,
project_id: @project, project_id: @project,
format: :csv format: :csv
),
graph_api_path: namespace_project_ci_daily_build_group_report_results_path(
namespace_id: @project.namespace,
project_id: @project,
format: :json
) )
} }
end end
......
...@@ -17,18 +17,22 @@ module Ci ...@@ -17,18 +17,22 @@ module Ci
return none unless can?(current_user, :read_build_report_results, project) return none unless can?(current_user, :read_build_report_results, project)
Ci::DailyBuildGroupReportResult.recent_results( Ci::DailyBuildGroupReportResult.recent_results(
{ query_params,
project_id: project, limit: limit
ref_path: ref_path,
date: start_date..end_date
},
limit: @limit
) )
end end
private private
attr_reader :current_user, :project, :ref_path, :start_date, :end_date attr_reader :current_user, :project, :ref_path, :start_date, :end_date, :limit
def query_params
{
project_id: project,
ref_path: ref_path,
date: start_date..end_date
}
end
def none def none
Ci::DailyBuildGroupReportResult.none Ci::DailyBuildGroupReportResult.none
......
# frozen_string_literal: true
module Ci
class DailyBuildGroupReportResultEntity < Grape::Entity
expose :date
::Ci::DailyBuildGroupReportResult::PARAM_TYPES.each do |type|
expose type, if: lambda { |report_result, options| options[:param_type] == type } do |report_result, options|
report_result.data[options[:param_type]]
end
end
end
end
# frozen_string_literal: true
module Ci
class DailyBuildGroupReportResultSerializer < BaseSerializer
entity ::Ci::DailyBuildGroupReportResultEntity
def represent(resource, opts = {})
group(resource).map do |group_name, data|
{
group_name: group_name,
data: super(data, opts)
}
end
end
private
def group(resource)
collect(resource).group_by(&:group_name)
end
def collect(resource)
return resource if resource.respond_to?(:group_by)
[resource]
end
end
end
...@@ -67,7 +67,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -67,7 +67,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
namespace :ci do namespace :ci do
resource :lint, only: [:show, :create] resource :lint, only: [:show, :create]
resources :daily_build_group_report_results, only: [:index], constraints: { format: 'csv' } resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ }
end end
namespace :settings do namespace :settings do
......
...@@ -14,9 +14,10 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do ...@@ -14,9 +14,10 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
before do before do
create_daily_coverage('rspec', 79.0, '2020-03-09') create_daily_coverage('rspec', 79.0, '2020-03-09')
create_daily_coverage('rspec', 77.0, '2020-03-08')
create_daily_coverage('karma', 81.0, '2019-12-10') create_daily_coverage('karma', 81.0, '2019-12-10')
create_daily_coverage('rspec', 67.0, '2019-12-09') create_daily_coverage('minitest', 67.0, '2019-12-09')
create_daily_coverage('karma', 71.0, '2019-12-09') create_daily_coverage('mocha', 71.0, '2019-12-09')
sign_in(user) sign_in(user)
...@@ -30,10 +31,33 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do ...@@ -30,10 +31,33 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
param_type: param_type, param_type: param_type,
start_date: start_date, start_date: start_date,
end_date: end_date, end_date: end_date,
format: :csv format: format
} }
end end
shared_examples_for 'validating param_type' do
context 'when given param_type is invalid' do
let(:param_type) { 'something_else' }
it 'responds with 422 error' do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
end
shared_examples_for 'ensuring policy' do
context 'when user is not allowed to read build report results' do
let(:allowed_to_read) { false }
it 'responds with 404 error' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when format is CSV' do
let(:format) { :csv }
it 'serves the results in CSV' do it 'serves the results in CSV' do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('text/csv; charset=utf-8') expect(response.headers['Content-Type']).to eq('text/csv; charset=utf-8')
...@@ -41,6 +65,7 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do ...@@ -41,6 +65,7 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
expect(csv_response).to eq([ expect(csv_response).to eq([
%w[date group_name coverage], %w[date group_name coverage],
['2020-03-09', 'rspec', '79.0'], ['2020-03-09', 'rspec', '79.0'],
['2020-03-08', 'rspec', '77.0'],
['2019-12-10', 'karma', '81.0'] ['2019-12-10', 'karma', '81.0']
]) ])
end end
...@@ -50,32 +75,68 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do ...@@ -50,32 +75,68 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
let(:end_date) { '2020-03-09' } let(:end_date) { '2020-03-09' }
it 'limits the result to 90 days from the given start_date' do it 'limits the result to 90 days from the given start_date' do
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('text/csv; charset=utf-8')
expect(csv_response).to eq([ expect(csv_response).to eq([
%w[date group_name coverage], %w[date group_name coverage],
['2020-03-09', 'rspec', '79.0'], ['2020-03-09', 'rspec', '79.0'],
['2020-03-08', 'rspec', '77.0'],
['2019-12-10', 'karma', '81.0'] ['2019-12-10', 'karma', '81.0']
]) ])
end end
end end
context 'when given param_type is invalid' do it_behaves_like 'validating param_type'
let(:param_type) { 'something_else' } it_behaves_like 'ensuring policy'
it 'responds with 422 error' do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end end
context 'when format is JSON' do
let(:format) { :json }
it 'serves the results in JSON' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq([
{
'group_name' => 'rspec',
'data' => [
{ 'date' => '2020-03-09', 'coverage' => 79.0 },
{ 'date' => '2020-03-08', 'coverage' => 77.0 }
]
},
{
'group_name' => 'karma',
'data' => [
{ 'date' => '2019-12-10', 'coverage' => 81.0 }
]
}
])
end end
context 'when user is not allowed to read build report results' do context 'when given date range spans more than 90 days' do
let(:allowed_to_read) { false } let(:start_date) { '2019-12-09' }
let(:end_date) { '2020-03-09' }
it 'responds with 404 error' do it 'limits the result to 90 days from the given start_date' do
expect(response).to have_gitlab_http_status(:not_found) expect(json_response).to eq([
{
'group_name' => 'rspec',
'data' => [
{ 'date' => '2020-03-09', 'coverage' => 79.0 },
{ 'date' => '2020-03-08', 'coverage' => 77.0 }
]
},
{
'group_name' => 'karma',
'data' => [
{ 'date' => '2019-12-10', 'coverage' => 81.0 }
]
}
])
end end
end end
it_behaves_like 'validating param_type'
it_behaves_like 'ensuring policy'
end
end end
def create_daily_coverage(group_name, coverage, date) def create_daily_coverage(group_name, coverage, date)
......
...@@ -49,8 +49,8 @@ RSpec.describe Projects::GraphsController do ...@@ -49,8 +49,8 @@ RSpec.describe Projects::GraphsController do
expect(assigns[:daily_coverage_options]).to eq( expect(assigns[:daily_coverage_options]).to eq(
base_params: { base_params: {
start_date: Time.current.to_date - 90.days, start_date: Date.current - 90.days,
end_date: Time.current.to_date, end_date: Date.current,
ref_path: project.repository.expand_ref('master'), ref_path: project.repository.expand_ref('master'),
param_type: 'coverage' param_type: 'coverage'
}, },
...@@ -58,6 +58,11 @@ RSpec.describe Projects::GraphsController do ...@@ -58,6 +58,11 @@ RSpec.describe Projects::GraphsController do
namespace_id: project.namespace, namespace_id: project.namespace,
project_id: project, project_id: project,
format: :csv format: :csv
),
graph_api_path: namespace_project_ci_daily_build_group_report_results_path(
namespace_id: project.namespace,
project_id: project,
format: :json
) )
) )
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Ci::DailyBuildGroupReportResultEntity do
let(:report_result) { double(date: '2020-05-20', group_name: 'rspec', data: { 'coverage' => 79.1 }) }
let(:entity) { described_class.new(report_result, param_type: param_type) }
let(:param_type) { 'coverage' }
describe '#as_json' do
subject { entity.as_json }
it { is_expected.to include(:date) }
it { is_expected.not_to include(:group_name) }
it { is_expected.to include(:coverage) }
context 'when given param_type is not allowed' do
let(:param_type) { 'something_else' }
it { is_expected.not_to include(:coverage) }
it { is_expected.not_to include(:something_else) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Ci::DailyBuildGroupReportResultSerializer do
let(:report_result) do
[
double(date: '2020-05-20', group_name: 'rspec', data: { 'coverage' => 79.1 }),
double(date: '2020-05-20', group_name: 'karma', data: { 'coverage' => 90.1 }),
double(date: '2020-05-19', group_name: 'rspec', data: { 'coverage' => 77.1 }),
double(date: '2020-05-19', group_name: 'karma', data: { 'coverage' => 89.1 })
]
end
let(:serializer) { described_class.new.represent(report_result, param_type: 'coverage') }
describe '#to_json' do
let(:json) { Gitlab::Json.parse(serializer.to_json) }
it 'returns an array of group results' do
expect(json).to eq([
{
'group_name' => 'rspec',
'data' => [
{ 'date' => '2020-05-20', 'coverage' => 79.1 },
{ 'date' => '2020-05-19', 'coverage' => 77.1 }
]
},
{
'group_name' => 'karma',
'data' => [
{ 'date' => '2020-05-20', 'coverage' => 90.1 },
{ 'date' => '2020-05-19', 'coverage' => 89.1 }
]
}
])
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