Commit 1d415d4f authored by Tetiana Chupryna's avatar Tetiana Chupryna Committed by Mayra Cabrera

Return dependency list with status

Change response schema and return not only a list of dependencies
but status and ci job path.
As well, replased mock data with dependency list report data.
parent caa32a6a
...@@ -3,42 +3,43 @@ ...@@ -3,42 +3,43 @@
module Projects module Projects
module Security module Security
class DependenciesController < Projects::ApplicationController class DependenciesController < Projects::ApplicationController
SORT_BY_PERMITTED_VALUES = %w(name type).freeze before_action :ensure_dependency_list_feature_available
SORT_PERMITTED_VALUES = %w(asc desc).freeze
before_action :ensure_bill_of_materials_feature_flag_enabled
def index def index
respond_to do |format| respond_to do |format|
format.json do format.json do
render json: paginated_dependecies render json: paginated_dependencies
end end
end end
end end
private private
def ensure_bill_of_materials_feature_flag_enabled def ensure_dependency_list_feature_available
render_404 unless Feature.enabled?(:bill_of_materials, default_enabled: false) render_404 unless project.feature_available?(:dependency_list)
end end
def found_dependencies def found_dependencies
::Security::DependenciesFinder.new(project: @project, params: query_params).execute @dependencies ||= pipeline ? service.execute : []
end
def paginated_dependencies
params[:page] ? Kaminari.paginate_array(found_dependencies).page(params[:page]) : found_dependencies
end
def pipeline
@pipeline ||= project.all_pipelines.latest_successful_for(project.default_branch)
end end
def query_params def query_params
params.permit(:sort, :sort_by).delete_if do |key, value| params.permit(:sort, :sort_by).delete_if do |key, value|
key == :sort_by && !value.in?(::Security::DependenciesFinder::SORT_BY_VALUES) || key == :sort_by && !value.in?(::Security::DependencyListService::SORT_BY_VALUES) ||
key == :sort && !value.in?(::Security::DependenciesFinder::SORT_VALUES) key == :sort && !value.in?(::Security::DependencyListService::SORT_VALUES)
end end
end end
# TODO: add proper implementation of edge cases handling def service
# format: { report: 'failed' } ::Security::DependencyListService.new(pipeline: pipeline, params: query_params)
# after we'll have more then just mock data
# reference: https://gitlab.com/gitlab-org/gitlab-ee/issues/10075#note_164915787
def paginated_dependecies
Kaminari.paginate_array(found_dependencies).page(params[:page])
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module Security module Security
class DependenciesFinder class DependencyListService
attr_accessor :params SORT_BY_VALUES = %w(name packager).freeze
attr_reader :project
SORT_BY_VALUES = %w(name type).freeze
SORT_VALUES = %w(asc desc).freeze SORT_VALUES = %w(asc desc).freeze
# @param project [Project] # @param pipeline [Ci::Pipeline]
# @param [Hash] params to sort dependencies # @param [Hash] params to sort dependencies
# @option params ['asc', 'desc'] :sort ('asc') Order # @option params ['asc', 'desc'] :sort ('asc') Order
# @option params ['name', 'type'] :sort_by ('name') Field to sort # @option params ['name', 'packager'] :sort_by ('name') Field to sort
def initialize(project:, params: {}) def initialize(pipeline:, params: {})
@project = project @pipeline = pipeline
@params = params @params = params
end end
...@@ -26,33 +23,15 @@ module Security ...@@ -26,33 +23,15 @@ module Security
private private
def init_collection attr_accessor :params, :pipeline
array = []
100.times { array << mock }
array
end
def fake_name
(0..16).map { ('a'..'z').to_a[rand 26] }.join
end
def mock def init_collection
{ pipeline.dependency_list_report.dependencies
name: fake_name,
type: %w(gem npm module).sample,
location: {
blob_path: 'gitlab-org/gitlab-ee/blob/master/Gemfile.lock#L1248'
},
version: '5.4.1',
requirements: [
'~>5.4.1'
]
}
end end
def sort(collection) def sort(collection)
if @params[:sort_by] == 'type' if @params[:sort_by] == 'packager'
collection.sort_by! { |a| a[:type] } collection.sort_by! { |a| a[:packager] }
else else
collection.sort_by! { |a| a[:name] } collection.sort_by! { |a| a[:name] }
end end
......
---
title: Use real data in `:project/security/dependencies` endpoint
merge_request: 13906
author:
type: changed
...@@ -19,35 +19,42 @@ describe Projects::Security::DependenciesController do ...@@ -19,35 +19,42 @@ describe Projects::Security::DependenciesController do
end end
context 'when feature is available' do context 'when feature is available' do
before do
stub_licensed_features(dependency_list: true)
end
context 'with existing report' do
let!(:pipeline) { create(:ee_ci_pipeline, :with_dependency_list_report, project: project) }
context 'without pagination params' do
it "returns a list of dependencies" do it "returns a list of dependencies" do
subject subject
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an(Array) expect(json_response).to be_a(Array)
expect(json_response.length).to eq 20 expect(json_response.length).to eq(21)
end
end end
context 'with params' do
it 'returns paginated list' do it 'returns paginated list' do
get :index, params: { namespace_id: project.namespace, project_id: project, page: 2 }, format: :json get :index, params: { namespace_id: project.namespace, project_id: project, page: 2 }, format: :json
expect(json_response.length).to eq 20 expect(json_response.length).to eq(1)
end end
it 'returns sorted list' do it 'returns sorted list' do
get :index, params: { namespace_id: project.namespace, project_id: project, sort_by: 'type', sort: 'desc' }, format: :json get :index, params: { namespace_id: project.namespace, project_id: project, sort_by: 'packager', sort: 'desc', page: 1 }, format: :json
sorted = json_response.sort_by { |a| a[:type] }.reverse
expect(json_response[0][:type]).to eq(sorted[0][:type]) expect(json_response.length).to eq(20)
expect(json_response[19][:type]).to eq(sorted[19][:type]) expect(json_response[0]['packager']).to eq('Ruby (Bundler)')
expect(json_response[19]['packager']).to eq('JavaScript (Yarn)')
end
end end
end end
context 'when feature is disabled' do
before do
stub_feature_flags(bill_of_materials: false)
end end
context 'when feature is not available' do
it 'returns 404' do it 'returns 404' do
subject subject
...@@ -55,5 +62,13 @@ describe Projects::Security::DependenciesController do ...@@ -55,5 +62,13 @@ describe Projects::Security::DependenciesController do
end end
end end
end end
context 'with unauthorized user' do
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(404)
end
end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Security::DependenciesFinder do
describe '#execute' do
let(:project) { create :project }
subject { described_class.new(project: project, params: params).execute }
context 'without params' do
let(:params) { {} }
it 'returns array of data sorted by names' do
result = subject.sort_by { |a| a[:name] }
is_expected.to be_an(Array)
expect(subject.size).to eq(100)
expect(subject.first[:name]).to eq(result.first[:name])
expect(subject.last[:name]).to eq(result.last[:name])
end
end
context 'with params' do
context 'sorted desc by types' do
let(:params) do
{
sort: 'desc',
sort_by: 'type'
}
end
it 'returns array of data properly sorted' do
result = subject.sort_by { |a| a[:type] }.reverse
is_expected.to be_an(Array)
expect(subject.size).to eq(100)
expect(subject.first[:type]).to eq(result.first[:type])
expect(subject.last[:type]).to eq(result.last[:type])
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Security::DependencyListService do
describe '#execute' do
let!(:pipeline) { create(:ee_ci_pipeline, :with_dependency_list_report) }
subject { described_class.new(pipeline: pipeline, params: params).execute }
before do
stub_licensed_features(dependency_list: true)
end
context 'without params' do
let(:params) { {} }
it 'returns array of dependencies' do
is_expected.to be_an(Array)
end
it 'is sorted by names by default' do
expect(subject.size).to eq(21)
expect(subject.first[:name]).to eq('async')
expect(subject.last[:name]).to eq('xpath.js')
end
end
context 'with params' do
context 'sorted desc by packagers' do
let(:params) do
{
sort: 'desc',
sort_by: 'packager'
}
end
it 'returns array of data properly sorted' do
expect(subject.first[:packager]).to eq('Ruby (Bundler)')
expect(subject.last[:packager]).to eq('JavaScript (Yarn)')
end
end
context 'sorted asc by packagers' do
let(:params) do
{
sort: 'asc',
sort_by: 'packager'
}
end
it 'returns array of data properly sorted' do
expect(subject.first[:packager]).to eq('JavaScript (Yarn)')
expect(subject.last[:packager]).to eq('Ruby (Bundler)')
end
end
context 'sorted desc by names' do
let(:params) do
{
sort: 'desc',
sort_by: 'name'
}
end
it 'returns array of data properly sorted' do
expect(subject.first[:name]).to eq('xpath.js')
expect(subject.last[:name]).to eq('async')
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