Commit 539f10f0 authored by Igor Drozdov's avatar Igor Drozdov

Remove LSIF API endpoints which aren't used

We used them for the first code intelligence iteration
Now we don't need them, so we can remove
parent 84022982
# 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