From 4644a2daf5ec5e86e2b2989f04e99e4f081f6fef Mon Sep 17 00:00:00 2001 From: Phil Hughes <me@iamphill.com> Date: Tue, 4 Jun 2019 14:38:18 +0100 Subject: [PATCH] Add web_url to tree entry in GraphQL API --- .../repository/components/table/index.vue | 1 + .../repository/components/table/row.vue | 7 ++++- .../repository/queries/getFiles.graphql | 2 ++ app/graphql/types/tree/blob_type.rb | 4 +++ app/graphql/types/tree/tree_entry_type.rb | 4 +++ app/graphql/types/tree/tree_type.rb | 10 ++++-- app/presenters/blob_presenter.rb | 4 +++ app/presenters/tree_entry_presenter.rb | 9 ++++++ .../graphql/representation/tree_entry.rb | 31 +++++++++++++++++++ .../repository/components/table/row_spec.js | 12 +++++++ spec/graphql/types/tree/blob_type_spec.rb | 2 +- .../types/tree/tree_entry_type_spec.rb | 2 +- .../graphql/representation/tree_entry_spec.rb | 20 ++++++++++++ spec/presenters/blob_presenter_spec.rb | 10 ++++++ spec/presenters/tree_entry_presenter_spec.rb | 16 ++++++++++ 15 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 app/presenters/tree_entry_presenter.rb create mode 100644 lib/gitlab/graphql/representation/tree_entry.rb create mode 100644 spec/lib/gitlab/graphql/representation/tree_entry_spec.rb create mode 100644 spec/presenters/tree_entry_presenter_spec.rb diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue index cccde1bb2784b..d2198bcccfee9 100644 --- a/app/assets/javascripts/repository/components/table/index.vue +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -134,6 +134,7 @@ export default { :current-path="path" :path="entry.flatPath" :type="entry.type" + :url="entry.webUrl" /> </template> </tbody> diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index 9a264bef87e8c..764882a793615 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -21,6 +21,11 @@ export default { type: String, required: true, }, + url: { + type: String, + required: false, + default: null, + }, }, computed: { routerLinkTo() { @@ -59,7 +64,7 @@ export default { <tr v-once :class="`file_${id}`" class="tree-item" @click="openRow"> <td class="tree-item-file-name"> <i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i> - <component :is="linkComponent" :to="routerLinkTo" class="str-truncated"> + <component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated"> {{ fullPath }} </component> <template v-if="isSubmodule"> diff --git a/app/assets/javascripts/repository/queries/getFiles.graphql b/app/assets/javascripts/repository/queries/getFiles.graphql index a9b61d285607a..7d92bc4645573 100644 --- a/app/assets/javascripts/repository/queries/getFiles.graphql +++ b/app/assets/javascripts/repository/queries/getFiles.graphql @@ -23,6 +23,7 @@ query getFiles( edges { node { ...TreeEntry + webUrl } } pageInfo { @@ -43,6 +44,7 @@ query getFiles( edges { node { ...TreeEntry + webUrl } } pageInfo { diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb index 230624201b06e..f2b7d5df2b23a 100644 --- a/app/graphql/types/tree/blob_type.rb +++ b/app/graphql/types/tree/blob_type.rb @@ -4,7 +4,11 @@ module Tree class BlobType < BaseObject implements Types::Tree::EntryType + present_using BlobPresenter + graphql_name 'Blob' + + field :web_url, GraphQL::STRING_TYPE, null: true end end end diff --git a/app/graphql/types/tree/tree_entry_type.rb b/app/graphql/types/tree/tree_entry_type.rb index d5cfb898aea6c..23ec2ef0ec2dc 100644 --- a/app/graphql/types/tree/tree_entry_type.rb +++ b/app/graphql/types/tree/tree_entry_type.rb @@ -4,8 +4,12 @@ module Tree class TreeEntryType < BaseObject implements Types::Tree::EntryType + present_using TreeEntryPresenter + graphql_name 'TreeEntry' description 'Represents a directory' + + field :web_url, GraphQL::STRING_TYPE, null: true end end end diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb index 1eb6c43972e64..1ee93ed95420a 100644 --- a/app/graphql/types/tree/tree_type.rb +++ b/app/graphql/types/tree/tree_type.rb @@ -4,9 +4,15 @@ module Tree class TreeType < BaseObject graphql_name 'Tree' - field :trees, Types::Tree::TreeEntryType.connection_type, null: false + field :trees, Types::Tree::TreeEntryType.connection_type, null: false, resolve: -> (obj, args, ctx) do + Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository) + end + field :submodules, Types::Tree::SubmoduleType.connection_type, null: false - field :blobs, Types::Tree::BlobType.connection_type, null: false + + field :blobs, Types::Tree::BlobType.connection_type, null: false, resolve: -> (obj, args, ctx) do + Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository) + end end end end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb index 6323c1b338985..c5675ef3ea322 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -13,4 +13,8 @@ def highlight(plain: nil) plain: plain ) end + + def web_url + Gitlab::Routing.url_helpers.project_blob_url(blob.repository.project, File.join(blob.commit_id, blob.path)) + end end diff --git a/app/presenters/tree_entry_presenter.rb b/app/presenters/tree_entry_presenter.rb new file mode 100644 index 0000000000000..7bb10cd1455ed --- /dev/null +++ b/app/presenters/tree_entry_presenter.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class TreeEntryPresenter < Gitlab::View::Presenter::Delegated + presents :tree + + def web_url + Gitlab::Routing.url_helpers.project_tree_url(tree.repository.project, File.join(tree.commit_id, tree.path)) + end +end diff --git a/lib/gitlab/graphql/representation/tree_entry.rb b/lib/gitlab/graphql/representation/tree_entry.rb new file mode 100644 index 0000000000000..7ea83db5876b5 --- /dev/null +++ b/lib/gitlab/graphql/representation/tree_entry.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + module Representation + class TreeEntry < SimpleDelegator + class << self + def decorate(entries, repository) + return if entries.nil? + + entries.map do |entry| + if entry.is_a?(TreeEntry) + entry + else + self.new(entry, repository) + end + end + end + end + + attr_accessor :repository + + def initialize(raw_entry, repository) + @repository = repository + + super(raw_entry) + end + end + end + end +end diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js index 6b4508c418ea5..a70dc7bb8668a 100644 --- a/spec/frontend/repository/components/table/row_spec.js +++ b/spec/frontend/repository/components/table/row_spec.js @@ -86,4 +86,16 @@ describe('Repository table row component', () => { expect(vm.find('.commit-sha').text()).toContain('1'); }); + + it('renders link with href', () => { + factory({ + id: '1', + path: 'test', + type: 'blob', + url: 'https://test.com', + currentPath: '/', + }); + + expect(vm.find('a').attributes('href')).toEqual('https://test.com'); + }); }); diff --git a/spec/graphql/types/tree/blob_type_spec.rb b/spec/graphql/types/tree/blob_type_spec.rb index fa29bb5fff79b..b12e214ca848a 100644 --- a/spec/graphql/types/tree/blob_type_spec.rb +++ b/spec/graphql/types/tree/blob_type_spec.rb @@ -5,5 +5,5 @@ describe Types::Tree::BlobType do it { expect(described_class.graphql_name).to eq('Blob') } - 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) } end diff --git a/spec/graphql/types/tree/tree_entry_type_spec.rb b/spec/graphql/types/tree/tree_entry_type_spec.rb index 397cabde8e545..ea1b6426034b5 100644 --- a/spec/graphql/types/tree/tree_entry_type_spec.rb +++ b/spec/graphql/types/tree/tree_entry_type_spec.rb @@ -5,5 +5,5 @@ describe Types::Tree::TreeEntryType do it { expect(described_class.graphql_name).to eq('TreeEntry') } - 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) } end diff --git a/spec/lib/gitlab/graphql/representation/tree_entry_spec.rb b/spec/lib/gitlab/graphql/representation/tree_entry_spec.rb new file mode 100644 index 0000000000000..d45e690160cf6 --- /dev/null +++ b/spec/lib/gitlab/graphql/representation/tree_entry_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Graphql::Representation::TreeEntry do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + + describe '.decorate' do + it 'returns NilClass when given nil' do + expect(described_class.decorate(nil, repository)).to be_nil + end + + it 'returns array of TreeEntry' do + entries = described_class.decorate(repository.tree.blobs, repository) + + expect(entries.first).to be_a(described_class) + end + end +end diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index bb1db9a3d51e9..eacf383be7dfa 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -14,6 +14,16 @@ end let(:blob) { Blob.new(git_blob) } + describe '.web_url' do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.blobs.first, repository) } + + subject { described_class.new(blob) } + + it { expect(subject.web_url).to eq("http://localhost/#{project.full_path}/blob/#{blob.commit_id}/#{blob.path}") } + end + describe '#highlight' do subject { described_class.new(blob) } diff --git a/spec/presenters/tree_entry_presenter_spec.rb b/spec/presenters/tree_entry_presenter_spec.rb new file mode 100644 index 0000000000000..d74ee5dc28f4c --- /dev/null +++ b/spec/presenters/tree_entry_presenter_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe TreeEntryPresenter do + include Gitlab::Routing.url_helpers + + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:tree) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.trees.first, repository) } + let(:presenter) { described_class.new(tree) } + + describe '.web_url' do + it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/tree/#{tree.commit_id}/#{tree.path}") } + end +end -- GitLab