Commit 0f468868 authored by Igor's avatar Igor Committed by Nick Thomas

Added submodule links to Submodule type in GraphQL API

This is part of migration of Folder View to Vue
parent b62c049f
...@@ -137,6 +137,7 @@ export default { ...@@ -137,6 +137,7 @@ export default {
:path="entry.flatPath" :path="entry.flatPath"
:type="entry.type" :type="entry.type"
:url="entry.webUrl" :url="entry.webUrl"
:submodule-tree-url="entry.treeUrl"
:lfs-oid="entry.lfsOid" :lfs-oid="entry.lfsOid"
/> />
</template> </template>
......
...@@ -62,6 +62,11 @@ export default { ...@@ -62,6 +62,11 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
submoduleTreeUrl: {
type: String,
required: false,
default: null,
},
}, },
data() { data() {
return { return {
...@@ -112,7 +117,7 @@ export default { ...@@ -112,7 +117,7 @@ export default {
</component> </component>
<gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge> <gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge>
<template v-if="isSubmodule"> <template v-if="isSubmodule">
@ <gl-link href="#" class="commit-sha">{{ shortSha }}</gl-link> @ <gl-link :href="submoduleTreeUrl" class="commit-sha">{{ shortSha }}</gl-link>
</template> </template>
</td> </td>
<td class="d-none d-sm-table-cell tree-commit"> <td class="d-none d-sm-table-cell tree-commit">
......
...@@ -35,6 +35,8 @@ query getFiles( ...@@ -35,6 +35,8 @@ query getFiles(
edges { edges {
node { node {
...TreeEntry ...TreeEntry
webUrl
treeUrl
} }
} }
pageInfo { pageInfo {
......
...@@ -7,6 +7,9 @@ module Types ...@@ -7,6 +7,9 @@ module Types
implements Types::Tree::EntryType implements Types::Tree::EntryType
graphql_name 'Submodule' graphql_name 'Submodule'
field :web_url, type: GraphQL::STRING_TYPE, null: true
field :tree_url, type: GraphQL::STRING_TYPE, null: true
end end
# rubocop: enable Graphql/AuthorizeTypes # rubocop: enable Graphql/AuthorizeTypes
end end
......
...@@ -15,7 +15,9 @@ module Types ...@@ -15,7 +15,9 @@ module Types
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository) Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
end end
field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(obj.submodules, obj)
end
field :blobs, Types::Tree::BlobType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do field :blobs, Types::Tree::BlobType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository) Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository)
......
...@@ -9,6 +9,10 @@ module SubmoduleHelper ...@@ -9,6 +9,10 @@ module SubmoduleHelper
def submodule_links(submodule_item, ref = nil, repository = @repository) def submodule_links(submodule_item, ref = nil, repository = @repository)
url = repository.submodule_url_for(ref, submodule_item.path) url = repository.submodule_url_for(ref, submodule_item.path)
submodule_links_for_url(submodule_item.id, url, repository)
end
def submodule_links_for_url(submodule_item_id, url, repository)
if url == '.' || url == './' if url == '.' || url == './'
url = File.join(Gitlab.config.gitlab.url, repository.project.full_path) url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end end
...@@ -31,13 +35,13 @@ module SubmoduleHelper ...@@ -31,13 +35,13 @@ module SubmoduleHelper
if self_url?(url, namespace, project) if self_url?(url, namespace, project)
[namespace_project_path(namespace, project), [namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, submodule_item.id)] namespace_project_tree_path(namespace, project, submodule_item_id)]
elsif relative_self_url?(url) elsif relative_self_url?(url)
relative_self_links(url, submodule_item.id, repository.project) relative_self_links(url, submodule_item_id, repository.project)
elsif github_dot_com_url?(url) elsif github_dot_com_url?(url)
standard_links('github.com', namespace, project, submodule_item.id) standard_links('github.com', namespace, project, submodule_item_id)
elsif gitlab_dot_com_url?(url) elsif gitlab_dot_com_url?(url)
standard_links('gitlab.com', namespace, project, submodule_item.id) standard_links('gitlab.com', namespace, project, submodule_item_id)
else else
[sanitize_submodule_url(url), nil] [sanitize_submodule_url(url), nil]
end end
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
class DiffFileBaseEntity < Grape::Entity class DiffFileBaseEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
include BlobHelper include BlobHelper
include SubmoduleHelper
include DiffHelper include DiffHelper
include TreeHelper include TreeHelper
include ChecksCollaboration include ChecksCollaboration
...@@ -12,12 +11,12 @@ class DiffFileBaseEntity < Grape::Entity ...@@ -12,12 +11,12 @@ class DiffFileBaseEntity < Grape::Entity
expose :content_sha expose :content_sha
expose :submodule?, as: :submodule expose :submodule?, as: :submodule
expose :submodule_link do |diff_file| expose :submodule_link do |diff_file, options|
memoized_submodule_links(diff_file).first memoized_submodule_links(diff_file, options).first
end end
expose :submodule_tree_url do |diff_file| expose :submodule_tree_url do |diff_file|
memoized_submodule_links(diff_file).last memoized_submodule_links(diff_file, options).last
end end
expose :edit_path, if: -> (_, options) { options[:merge_request] } do |diff_file| expose :edit_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
...@@ -92,10 +91,10 @@ class DiffFileBaseEntity < Grape::Entity ...@@ -92,10 +91,10 @@ class DiffFileBaseEntity < Grape::Entity
private private
def memoized_submodule_links(diff_file) def memoized_submodule_links(diff_file, options)
strong_memoize(:submodule_links) do strong_memoize(:submodule_links) do
if diff_file.submodule? if diff_file.submodule?
submodule_links(diff_file.blob, diff_file.content_sha, diff_file.repository) options[:submodule_links].for(diff_file.blob, diff_file.content_sha)
else else
[] []
end end
......
...@@ -64,7 +64,10 @@ class DiffsEntity < Grape::Entity ...@@ -64,7 +64,10 @@ class DiffsEntity < Grape::Entity
merge_request_path(merge_request, format: :diff) merge_request_path(merge_request, format: :diff)
end end
expose :diff_files, using: DiffFileEntity expose :diff_files do |diffs, options|
submodule_links = Gitlab::SubmoduleLinks.new(merge_request.project.repository)
DiffFileEntity.represent(diffs.diff_files, options.merge(submodule_links: submodule_links))
end
expose :merge_request_diffs, using: MergeRequestDiffEntity, if: -> (_, options) { options[:merge_request_diffs]&.any? } do |diffs| expose :merge_request_diffs, using: MergeRequestDiffEntity, if: -> (_, options) { options[:merge_request_diffs]&.any? } do |diffs|
options[:merge_request_diffs] options[:merge_request_diffs]
......
...@@ -2,4 +2,18 @@ ...@@ -2,4 +2,18 @@
class DiscussionSerializer < BaseSerializer class DiscussionSerializer < BaseSerializer
entity DiscussionEntity entity DiscussionEntity
def represent(resource, opts = {}, entity_class = nil)
super(resource, with_additional_opts(opts), entity_class)
end
private
def with_additional_opts(opts)
additional_opts = {
submodule_links: Gitlab::SubmoduleLinks.new(@request.project.repository)
}
opts.merge(additional_opts)
end
end end
# frozen_string_literal: true
class SubmoduleEntity < Grape::Entity
include RequestAwareEntity
expose :id, :path, :name, :mode
expose :icon do |blob|
'archive'
end
expose :url do |blob|
submodule_links(blob, request).first
end
expose :tree_url do |blob|
submodule_links(blob, request).last
end
private
def submodule_links(blob, request)
@submodule_links ||= SubmoduleHelper.submodule_links(blob, request.ref, request.repository)
end
end
...@@ -464,6 +464,18 @@ module Gitlab ...@@ -464,6 +464,18 @@ module Gitlab
end end
end end
# Returns path to url mappings for submodules
#
# Ex.
# @repository.submodule_urls_for('master')
# # => { 'rack' => 'git@localhost:rack.git' }
#
def submodule_urls_for(ref)
wrapped_gitaly_errors do
gitaly_submodule_urls_for(ref)
end
end
# Return total commits count accessible from passed ref # Return total commits count accessible from passed ref
def commit_count(ref) def commit_count(ref)
wrapped_gitaly_errors do wrapped_gitaly_errors do
...@@ -1059,12 +1071,16 @@ module Gitlab ...@@ -1059,12 +1071,16 @@ module Gitlab
return unless commit_object && commit_object.type == :COMMIT return unless commit_object && commit_object.type == :COMMIT
urls = gitaly_submodule_urls_for(ref)
urls && urls[path]
end
def gitaly_submodule_urls_for(ref)
gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
return unless gitmodules return unless gitmodules
found_module = GitmodulesParser.new(gitmodules.data).parse[path] submodules = GitmodulesParser.new(gitmodules.data).parse
submodules.transform_values { |submodule| submodule['url'] }
found_module && found_module['url']
end end
# Returns true if the given ref name exists # Returns true if the given ref name exists
......
# frozen_string_literal: true
module Gitlab
module Graphql
module Representation
class SubmoduleTreeEntry < SimpleDelegator
class << self
def decorate(submodules, tree)
repository = tree.repository
submodule_links = Gitlab::SubmoduleLinks.new(repository)
submodules.map do |submodule|
self.new(submodule, submodule_links.for(submodule, tree.sha))
end
end
end
def initialize(submodule, submodule_links)
@submodule_links = submodule_links
super(submodule)
end
def web_url
@submodule_links.first
end
def tree_url
@submodule_links.last
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
class SubmoduleLinks
include Gitlab::Utils::StrongMemoize
def initialize(repository)
@repository = repository
end
def for(submodule, sha)
submodule_url = submodule_url_for(sha)[submodule.path]
SubmoduleHelper.submodule_links_for_url(submodule.id, submodule_url, repository)
end
private
attr_reader :repository
def submodule_url_for(sha)
strong_memoize(:"submodule_links_for_#{sha}") do
repository.submodule_urls_for(sha)
end
end
end
end
import { shallowMount, RouterLinkStub } from '@vue/test-utils'; import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlBadge } from '@gitlab/ui'; import { GlBadge, GlLink } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue'; import TableRow from '~/repository/components/table/row.vue';
...@@ -142,4 +142,18 @@ describe('Repository table row component', () => { ...@@ -142,4 +142,18 @@ describe('Repository table row component', () => {
expect(vm.find(GlBadge).exists()).toBe(true); expect(vm.find(GlBadge).exists()).toBe(true);
}); });
it('renders commit and web links with href for submodule', () => {
factory({
id: '1',
path: 'test',
type: 'commit',
url: 'https://test.com',
submoduleTreeUrl: 'https://test.com/commit',
currentPath: '/',
});
expect(vm.find('a').attributes('href')).toEqual('https://test.com');
expect(vm.find(GlLink).attributes('href')).toEqual('https://test.com/commit');
});
}); });
...@@ -5,5 +5,5 @@ require 'spec_helper' ...@@ -5,5 +5,5 @@ require 'spec_helper'
describe Types::Tree::SubmoduleType do describe Types::Tree::SubmoduleType do
it { expect(described_class.graphql_name).to eq('Submodule') } it { expect(described_class.graphql_name).to eq('Submodule') }
it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path) } it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url, :tree_url) }
end end
...@@ -256,6 +256,22 @@ describe Gitlab::Git::Repository, :seed_helper do ...@@ -256,6 +256,22 @@ describe Gitlab::Git::Repository, :seed_helper do
end end
end end
describe '#submodule_urls_for' do
let(:ref) { 'master' }
it 'returns url mappings for submodules' do
urls = repository.submodule_urls_for(ref)
expect(urls).to eq({
"deeper/nested/six" => "git://github.com/randx/six.git",
"gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
"gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
"nested/six" => "git://github.com/randx/six.git",
"six" => "git://github.com/randx/six.git"
})
end
end
describe '#commit_count' do describe '#commit_count' do
it { expect(repository.commit_count("master")).to eq(25) } it { expect(repository.commit_count("master")).to eq(25) }
it { expect(repository.commit_count("feature")).to eq(9) } it { expect(repository.commit_count("feature")).to eq(9) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Graphql::Representation::SubmoduleTreeEntry do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
describe '.decorate' do
let(:submodules) { repository.tree.submodules }
it 'returns array of SubmoduleTreeEntry' do
entries = described_class.decorate(submodules, repository.tree)
expect(entries.first).to be_a(described_class)
expect(entries.map(&:web_url)).to contain_exactly(
"https://gitlab.com/gitlab-org/gitlab-grack",
"https://github.com/gitlabhq/gitlab-shell",
"https://github.com/randx/six"
)
expect(entries.map(&:tree_url)).to contain_exactly(
"https://gitlab.com/gitlab-org/gitlab-grack/tree/645f6c4c82fd3f5e06f67134450a570b795e55a6",
"https://github.com/gitlabhq/gitlab-shell/tree/79bceae69cb5750d6567b223597999bfa91cb3b9",
"https://github.com/randx/six/tree/409f37c4f05865e4fb208c771485f211a22c4c2d"
)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe DiffFileBaseEntity do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
context 'diff for a changed submodule' do
let(:commit_sha_with_changed_submodule) do
"cfe32cf61b73a0d5e9f13e774abde7ff789b1660"
end
let(:commit) { project.commit(commit_sha_with_changed_submodule) }
let(:diff_file) { commit.diffs.diff_files.to_a.last }
let(:options) { { request: {}, submodule_links: Gitlab::SubmoduleLinks.new(repository) } }
let(:entity) { described_class.new(diff_file, options).as_json }
it do
expect(entity[:submodule]).to eq(true)
expect(entity[:submodule_link]).to eq("https://github.com/randx/six")
expect(entity[:submodule_tree_url]).to eq(
"https://github.com/randx/six/tree/409f37c4f05865e4fb208c771485f211a22c4c2d"
)
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