Commit 8453658d authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'id-remove-lsif-api-endpoints' into 'master'

Remove LSIF API endpoints which aren't used

See merge request gitlab-org/gitlab!32325
parents 90f9fc45 539f10f0
# frozen_string_literal: true
module Projects
class LsifDataService
attr_reader :file, :project, :commit_id, :docs,
:doc_ranges, :ranges, :def_refs, :hover_refs
CACHE_EXPIRE_IN = 1.hour
def initialize(file, project, commit_id)
@file = file
@project = project
@commit_id = commit_id
fetch_data!
end
def execute(path)
doc_id = find_doc_id(docs, path)
dir_absolute_path = docs[doc_id]&.delete_suffix(path)
doc_ranges[doc_id]&.map do |range_id|
location, ref_id = ranges[range_id].values_at('loc', 'ref_id')
line_data, column_data = location
{
start_line: line_data.first,
end_line: line_data.last,
start_char: column_data.first,
end_char: column_data.last,
definition_url: definition_url_for(def_refs[ref_id], dir_absolute_path),
hover: highlighted_hover(hover_refs[ref_id])
}
end
end
private
def fetch_data
Rails.cache.fetch("project:#{project.id}:lsif:#{commit_id}", expires_in: CACHE_EXPIRE_IN) do
data = nil
file.open do |stream|
Zlib::GzipReader.wrap(stream) do |gz_stream|
data = Gitlab::Json.parse(gz_stream.read)
end
end
data
end
end
def fetch_data!
data = fetch_data
@docs = data['docs']
@doc_ranges = data['doc_ranges']
@ranges = data['ranges']
@def_refs = data['def_refs']
@hover_refs = data['hover_refs']
end
def find_doc_id(docs, path)
docs.reduce(nil) do |doc_id, (id, doc_path)|
next doc_id unless doc_path =~ /#{path}$/
if doc_id.nil? || docs[doc_id].size > doc_path.size
doc_id = id
end
doc_id
end
end
def definition_url_for(ref_id, dir_absolute_path)
return unless range = ranges[ref_id]
def_doc_id, location = range.values_at('doc_id', 'loc')
localized_doc_url = docs[def_doc_id].delete_prefix(dir_absolute_path)
# location is stored as [[start_line, end_line], [start_char, end_char]]
start_line = location.first.first
line_anchor = "L#{start_line + 1}"
definition_ref_path = [commit_id, localized_doc_url].join('/')
Gitlab::Routing.url_helpers.project_blob_path(project, definition_ref_path, anchor: line_anchor)
end
def highlighted_hover(hovers)
hovers&.map do |hover|
# Documentation for a method which is added as comments on top of the method
# is stored as a raw string value in LSIF file
next { value: hover } unless hover.is_a?(Hash)
value = Gitlab::Highlight.highlight(nil, hover['value'], language: hover['language'])
{ language: hover['language'], value: value }
end
end
end
end
......@@ -159,7 +159,6 @@ module API
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
mount ::API::LsifData
mount ::API::Markdown
mount ::API::Members
mount ::API::MergeRequestDiffs
......
# frozen_string_literal: true
module API
class LsifData < Grape::API
MAX_FILE_SIZE = 10.megabytes
before do
not_found! if Feature.disabled?(:code_navigation, user_project)
end
params do
requires :id, type: String, desc: 'The ID of a project'
requires :commit_id, type: String, desc: 'The ID of a commit'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
segment ':id/commits/:commit_id' do
params do
requires :paths, type: Array, desc: 'The paths of the files'
end
get 'lsif/info' do
authorize! :download_code, user_project
artifact =
Ci::JobArtifact
.with_file_types(['lsif'])
.for_sha(params[:commit_id], @project.id)
.last
not_found! unless artifact
authorize! :read_pipeline, artifact.job.pipeline
file_too_large! if artifact.file.cached_size > MAX_FILE_SIZE
service = ::Projects::LsifDataService.new(artifact.file, @project, params[:commit_id])
params[:paths].to_h { |path| [path, service.execute(path)] }
end
end
end
end
end
......@@ -371,10 +371,9 @@ describe Projects::ArtifactsController do
end
context 'when the artifact is zip' do
let!(:artifact) { create(:ci_job_artifact, :lsif, job: job, file_path: Rails.root.join("spec/fixtures/#{file_name}")) }
let!(:artifact) { create(:ci_job_artifact, :lsif, job: job) }
let(:path) { 'lsif/main.go.json' }
let(:file_name) { 'lsif.json.zip' }
let(:archive_matcher) { file_name }
let(:archive_matcher) { 'lsif.json.zip' }
let(:query_params) { super().merge(file_type: :lsif, path: path) }
it_behaves_like 'a valid file' do
......
......@@ -233,12 +233,9 @@ FactoryBot.define do
file_type { :lsif }
file_format { :zip }
transient do
file_path { Rails.root.join('spec/fixtures/lsif.json.gz') }
end
after(:build) do |artifact, evaluator|
artifact.file = fixture_file_upload(evaluator.file_path, 'application/x-gzip')
artifact.file = fixture_file_upload(
Rails.root.join('spec/fixtures/lsif.json.zip'), 'application/zip')
end
end
......
# frozen_string_literal: true
require "spec_helper"
describe API::LsifData do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let(:commit) { project.commit }
describe 'GET lsif/info' do
subject do
endpoint_path = "/projects/#{project.id}/commits/#{commit.id}/lsif/info"
get api(endpoint_path, user), params: { paths: ['main.go', 'morestrings/reverse.go'] }
response
end
context 'user does not have access to the project' do
before do
project.add_guest(user)
end
it { is_expected.to have_gitlab_http_status(:forbidden) }
end
context 'user has access to the project' do
before do
project.add_reporter(user)
end
context 'there is no job artifact for the passed commit' do
it { is_expected.to have_gitlab_http_status(:not_found) }
end
context 'lsif data is stored as a job artifact' do
let!(:pipeline) { create(:ci_pipeline, project: project, sha: commit.id) }
let!(:artifact) { create(:ci_job_artifact, :lsif, job: create(:ci_build, pipeline: pipeline)) }
context 'code_navigation feature is disabled' do
before do
stub_feature_flags(code_navigation: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
end
it 'returns code navigation info for a given path', :aggregate_failures do
expect(subject).to have_gitlab_http_status(:ok)
data_for_main = response.parsed_body['main.go']
expect(data_for_main.last).to eq({
'end_char' => 18,
'end_line' => 8,
'start_char' => 13,
'start_line' => 8,
'definition_url' => project_blob_path(project, "#{commit.id}/morestrings/reverse.go", anchor: 'L5'),
'hover' => [{
'language' => 'go',
'value' => Gitlab::Highlight.highlight(nil, 'func Func2(i int) string', language: 'go')
}]
})
data_for_reverse = response.parsed_body['morestrings/reverse.go']
expect(data_for_reverse.last).to eq({
'end_char' => 9,
'end_line' => 7,
'start_char' => 8,
'start_line' => 7,
'definition_url' => project_blob_path(project, "#{commit.id}/morestrings/reverse.go", anchor: 'L6'),
'hover' => [{
'language' => 'go',
'value' => Gitlab::Highlight.highlight(nil, 'var b string', language: 'go')
}]
})
end
context 'the stored file is too large' do
before do
allow_any_instance_of(JobArtifactUploader).to receive(:cached_size).and_return(20.megabytes)
end
it { is_expected.to have_gitlab_http_status(:payload_too_large) }
end
context 'the user does not have access to the pipeline' do
let(:project) { create(:project, :repository, builds_access_level: ProjectFeature::DISABLED) }
it { is_expected.to have_gitlab_http_status(:forbidden) }
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Projects::LsifDataService do
let(:artifact) { create(:ci_job_artifact, :lsif) }
let(:project) { build_stubbed(:project) }
let(:path) { 'main.go' }
let(:commit_id) { Digest::SHA1.hexdigest(SecureRandom.hex) }
let(:service) { described_class.new(artifact.file, project, commit_id) }
describe '#execute' do
def highlighted_value(value)
[{ language: 'go', value: Gitlab::Highlight.highlight(nil, value, language: 'go') }]
end
context 'fetched lsif file', :use_clean_rails_memory_store_caching do
it 'is cached' do
service.execute(path)
cached_data = Rails.cache.fetch("project:#{project.id}:lsif:#{commit_id}")
expect(cached_data.keys).to eq(%w[def_refs doc_ranges docs hover_refs ranges])
end
end
context 'for main.go' do
let(:path_prefix) { "/#{project.full_path}/-/blob/#{commit_id}" }
it 'returns lsif ranges for the file' do
expect(service.execute(path)).to eq([
{
end_char: 9,
end_line: 6,
start_char: 5,
start_line: 6,
definition_url: "#{path_prefix}/main.go#L7",
hover: highlighted_value('func main()')
},
{
end_char: 36,
end_line: 3,
start_char: 1,
start_line: 3,
definition_url: "#{path_prefix}/main.go#L4",
hover: highlighted_value('package "github.com/user/hello/morestrings" ("github.com/user/hello/morestrings")')
},
{
end_char: 12,
end_line: 7,
start_char: 1,
start_line: 7,
definition_url: "#{path_prefix}/main.go#L4",
hover: highlighted_value('package "github.com/user/hello/morestrings" ("github.com/user/hello/morestrings")')
},
{
end_char: 20,
end_line: 7,
start_char: 13,
start_line: 7,
definition_url: "#{path_prefix}/morestrings/reverse.go#L11",
hover: highlighted_value('func Reverse(s string) string') + [{ value: "This method reverses a string \n\n" }]
},
{
end_char: 12,
end_line: 8,
start_char: 1,
start_line: 8,
definition_url: "#{path_prefix}/main.go#L4",
hover: highlighted_value('package "github.com/user/hello/morestrings" ("github.com/user/hello/morestrings")')
},
{
end_char: 18,
end_line: 8,
start_char: 13,
start_line: 8,
definition_url: "#{path_prefix}/morestrings/reverse.go#L5",
hover: highlighted_value('func Func2(i int) string')
}
])
end
end
context 'for morestring/reverse.go' do
let(:path) { 'morestrings/reverse.go' }
it 'returns lsif ranges for the file' do
expect(service.execute(path).first).to eq({
end_char: 2,
end_line: 11,
start_char: 1,
start_line: 11,
definition_url: "/#{project.full_path}/-/blob/#{commit_id}/morestrings/reverse.go#L12",
hover: highlighted_value('var a string')
})
end
end
context 'for an unknown file' do
let(:path) { 'unknown.go' }
it 'returns nil' do
expect(service.execute(path)).to eq(nil)
end
end
end
describe '#doc_id' do
context 'when the passed path matches multiple files' do
let(:path) { 'check/main.go' }
let(:docs) do
{
1 => 'cmd/check/main.go',
2 => 'cmd/command.go',
3 => 'check/main.go',
4 => 'cmd/nested/check/main.go'
}
end
it 'fetches the document with the shortest absolute path' do
expect(service.__send__(:find_doc_id, docs, path)).to eq(3)
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