Commit 1063d057 authored by Nick Thomas's avatar Nick Thomas

Give the template finders a uniform interface

Two changes in this commit:

* Template finders now take a `project` (currently unused)
* LicenseTemplateFinder now respects the `name` parameter
parent 89bcbdca
...@@ -5,23 +5,38 @@ ...@@ -5,23 +5,38 @@
# Used to find license templates, which may come from a variety of external # Used to find license templates, which may come from a variety of external
# sources # sources
# #
# Arguments: # Params can be any of the following:
# popular: boolean. When set to true, only "popular" licenses are shown. When # popular: boolean. When set to true, only "popular" licenses are shown. When
# false, all licenses except popular ones are shown. When nil (the # false, all licenses except popular ones are shown. When nil (the
# default), *all* licenses will be shown. # default), *all* licenses will be shown.
# name: string. If set, return a single license matching that name (or nil)
class LicenseTemplateFinder class LicenseTemplateFinder
include Gitlab::Utils::StrongMemoize
prepend ::EE::LicenseTemplateFinder prepend ::EE::LicenseTemplateFinder
attr_reader :params attr_reader :project, :params
def initialize(params = {}) def initialize(project, params = {})
@project = project
@params = params @params = params
end end
def execute def execute
if params[:name]
vendored_licenses.find { |template| template.key == params[:name] }
else
vendored_licenses
end
end
private
def vendored_licenses
strong_memoize(:vendored_licenses) do
Licensee::License.all(featured: popular_only?).map do |license| Licensee::License.all(featured: popular_only?).map do |license|
LicenseTemplate.new( LicenseTemplate.new(
id: license.key, key: license.key,
name: license.name, name: license.name,
nickname: license.nickname, nickname: license.nickname,
category: (license.featured? ? :Popular : :Other), category: (license.featured? ? :Popular : :Other),
...@@ -31,8 +46,7 @@ class LicenseTemplateFinder ...@@ -31,8 +46,7 @@ class LicenseTemplateFinder
) )
end end
end end
end
private
def popular_only? def popular_only?
params.fetch(:popular, nil) params.fetch(:popular, nil)
......
# frozen_string_literal: true # frozen_string_literal: true
class TemplateFinder class TemplateFinder
include Gitlab::Utils::StrongMemoize
prepend ::EE::TemplateFinder prepend ::EE::TemplateFinder
VENDORED_TEMPLATES = { VENDORED_TEMPLATES = HashWithIndifferentAccess.new(
dockerfiles: ::Gitlab::Template::DockerfileTemplate, dockerfiles: ::Gitlab::Template::DockerfileTemplate,
gitignores: ::Gitlab::Template::GitignoreTemplate, gitignores: ::Gitlab::Template::GitignoreTemplate,
gitlab_ci_ymls: ::Gitlab::Template::GitlabCiYmlTemplate gitlab_ci_ymls: ::Gitlab::Template::GitlabCiYmlTemplate
}.freeze ).freeze
class << self class << self
def build(type, params = {}) def build(type, project, params = {})
if type == :licenses if type.to_s == 'licenses'
LicenseTemplateFinder.new(params) # rubocop: disable CodeReuse/Finder LicenseTemplateFinder.new(project, params) # rubocop: disable CodeReuse/Finder
else else
new(type, params) new(type, project, params)
end end
end end
end end
attr_reader :type, :params attr_reader :type, :project, :params
attr_reader :vendored_templates attr_reader :vendored_templates
private :vendored_templates private :vendored_templates
def initialize(type, params = {}) def initialize(type, project, params = {})
@type = type @type = type
@project = project
@params = params @params = params
@vendored_templates = VENDORED_TEMPLATES.fetch(type) @vendored_templates = VENDORED_TEMPLATES.fetch(type)
......
...@@ -159,10 +159,6 @@ module BlobHelper ...@@ -159,10 +159,6 @@ module BlobHelper
end end
end end
def licenses_for_select
@licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses).execute)
end
def ref_project def ref_project
@ref_project ||= @target_project || @project @ref_project ||= @target_project || @project
end end
...@@ -173,22 +169,26 @@ module BlobHelper ...@@ -173,22 +169,26 @@ module BlobHelper
categories.each_with_object({}) do |category, hash| categories.each_with_object({}) do |category, hash|
hash[category] = grouped[category].map do |item| hash[category] = grouped[category].map do |item|
{ name: item.name, id: item.id } { name: item.name, id: item.key }
end end
end end
end end
private :template_dropdown_names private :template_dropdown_names
def gitignore_names def licenses_for_select(project = @project)
@gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores).execute) @licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses, project).execute)
end
def gitignore_names(project = @project)
@gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores, project).execute)
end end
def gitlab_ci_ymls def gitlab_ci_ymls(project = @project)
@gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls).execute) @gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls, project).execute)
end end
def dockerfile_names def dockerfile_names(project = @project)
@dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles).execute) @dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles, project).execute)
end end
def blob_editor_paths def blob_editor_paths
......
...@@ -12,12 +12,10 @@ class LicenseTemplate ...@@ -12,12 +12,10 @@ class LicenseTemplate
(fullname|name\sof\s(author|copyright\sowner)) (fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]}xi.freeze [\>\}\]]}xi.freeze
attr_reader :id, :name, :category, :nickname, :url, :meta attr_reader :key, :name, :category, :nickname, :url, :meta
alias_method :key, :id def initialize(key:, name:, category:, content:, nickname: nil, url: nil, meta: {})
@key = key
def initialize(id:, name:, category:, content:, nickname: nil, url: nil, meta: {})
@id = id
@name = name @name = name
@category = category @category = category
@content = content @content = content
......
module EE module EE
module LicenseTemplateFinder module LicenseTemplateFinder
include ::Gitlab::Utils::StrongMemoize
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :execute override :execute
def execute def execute
return super unless custom_templates? return super unless custom_templates?
extra = custom_licenses.map do |template| if params[:name]
LicenseTemplate.new( custom_template || super
id: template.name, else
name: template.name, custom_templates + super
nickname: template.name,
category: :Custom,
content: -> { template.content }
)
end end
extra.push(*super)
end end
private private
def custom_templates
templates_for(template_project).map do |template|
translate(template, category: :Custom)
end
end
def custom_template
template = template_for(template_project, params[:name])
translate(template, category: :Custom)
rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
nil
end
def custom_templates? def custom_templates?
!popular_only? && !popular_only? &&
::License.feature_available?(:custom_file_templates) && ::License.feature_available?(:custom_file_templates) &&
template_project.present? template_project.present?
end end
def custom_licenses
::Gitlab::Template::CustomLicenseTemplate.all(template_project)
end
def template_project def template_project
strong_memoize(:template_project) { ::Gitlab::CurrentSettings.file_template_project } strong_memoize(:template_project) { ::Gitlab::CurrentSettings.file_template_project }
end end
def templates_for(project)
return [] unless project
::Gitlab::Template::CustomLicenseTemplate.all(project)
end
def template_for(project, name)
return unless project
::Gitlab::Template::CustomLicenseTemplate.find(name, project)
end
def translate(template, category:)
return unless template
LicenseTemplate.new(
key: template.key,
name: template.name,
nickname: template.name,
category: category,
content: -> { template.content }
)
end
end end
end end
module EE module EE
module TemplateFinder module TemplateFinder
include ::Gitlab::Utils::StrongMemoize
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
CUSTOM_TEMPLATES = { CUSTOM_TEMPLATES = HashWithIndifferentAccess.new(
dockerfiles: ::Gitlab::Template::CustomDockerfileTemplate, dockerfiles: ::Gitlab::Template::CustomDockerfileTemplate,
gitignores: ::Gitlab::Template::CustomGitignoreTemplate, gitignores: ::Gitlab::Template::CustomGitignoreTemplate,
gitlab_ci_ymls: ::Gitlab::Template::CustomGitlabCiYmlTemplate gitlab_ci_ymls: ::Gitlab::Template::CustomGitlabCiYmlTemplate
}.freeze ).freeze
attr_reader :custom_templates attr_reader :custom_templates
private :custom_templates private :custom_templates
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe LicenseTemplateFinder do describe LicenseTemplateFinder do
describe '#execute' do describe '#execute' do
subject(:result) { described_class.new(params).execute } subject(:result) { described_class.new(nil, params).execute }
let(:params) { {} } let(:params) { {} }
......
...@@ -24,7 +24,7 @@ describe TemplateFinder do ...@@ -24,7 +24,7 @@ describe TemplateFinder do
end end
with_them do with_them do
subject(:result) { described_class.new(type, params).execute } subject(:result) { described_class.new(type, nil, params).execute }
context 'specifying name' do context 'specifying name' do
let(:params) { { name: custom_name } } let(:params) { { name: custom_name } }
......
...@@ -20,10 +20,10 @@ describe BlobHelper do ...@@ -20,10 +20,10 @@ describe BlobHelper do
expect(Gitlab::Template::CustomLicenseTemplate) expect(Gitlab::Template::CustomLicenseTemplate)
.to receive(:all) .to receive(:all)
.with(project) .with(project)
.and_return([OpenStruct.new(name: "name")]) .and_return([OpenStruct.new(key: 'name', name: 'Name')])
expect(categories).to contain_exactly(:Popular, :Other, :Custom) expect(categories).to contain_exactly(:Popular, :Other, :Custom)
expect(custom).to contain_exactly({ name: "name", id: "name" }) expect(custom).to contain_exactly({ id: 'name', name: 'Name' })
expect(popular).to be_present expect(popular).to be_present
expect(other).to be_present expect(other).to be_present
end end
......
...@@ -35,7 +35,7 @@ module API ...@@ -35,7 +35,7 @@ module API
popular = declared(params)[:popular] popular = declared(params)[:popular]
popular = to_boolean(popular) if popular.present? popular = to_boolean(popular) if popular.present?
templates = TemplateFinder.build(:licenses, popular: popular).execute templates = TemplateFinder.build(:licenses, nil, popular: popular).execute
present paginate(::Kaminari.paginate_array(templates)), with: ::API::Entities::License present paginate(::Kaminari.paginate_array(templates)), with: ::API::Entities::License
end end
...@@ -48,8 +48,7 @@ module API ...@@ -48,8 +48,7 @@ module API
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
end end
get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do
templates = TemplateFinder.build(:licenses).execute template = TemplateFinder.build(:licenses, nil, name: params[:name]).execute
template = templates.find { |template| template.key == params[:name] }
not_found!('License') unless template.present? not_found!('License') unless template.present?
...@@ -72,7 +71,7 @@ module API ...@@ -72,7 +71,7 @@ module API
use :pagination use :pagination
end end
get "templates/#{template_type}" do get "templates/#{template_type}" do
templates = ::Kaminari.paginate_array(TemplateFinder.new(template_type).execute) templates = ::Kaminari.paginate_array(TemplateFinder.build(template_type, nil).execute)
present paginate(templates), with: Entities::TemplatesList present paginate(templates), with: Entities::TemplatesList
end end
...@@ -84,7 +83,7 @@ module API ...@@ -84,7 +83,7 @@ module API
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
end end
get "templates/#{template_type}/:name" do get "templates/#{template_type}/:name" do
finder = TemplateFinder.build(template_type, name: declared(params)[:name]) finder = TemplateFinder.build(template_type, nil, name: declared(params)[:name])
new_template = finder.execute new_template = finder.execute
render_response(template_type, new_template) render_response(template_type, new_template)
......
...@@ -12,14 +12,21 @@ module Gitlab ...@@ -12,14 +12,21 @@ module Gitlab
def name def name
File.basename(@path, self.class.extension) File.basename(@path, self.class.extension)
end end
alias_method :id, :name alias_method :key, :name
def content def content
@finder.read(@path) @finder.read(@path)
end end
# Present for compatibility with license templates, which can replace text
# like `[fullname]` with a user-specified string. This is a no-op for
# other templates
def resolve!(_placeholders = {})
self
end
def to_json def to_json
{ name: name, content: content } { key: key, name: name, content: content }
end end
def <=>(other) def <=>(other)
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe LicenseTemplateFinder do describe LicenseTemplateFinder do
describe '#execute' do describe '#execute' do
subject(:result) { described_class.new(params).execute } subject(:result) { described_class.new(nil, params).execute }
let(:categories) { categorised_licenses.keys } let(:categories) { categorised_licenses.keys }
let(:categorised_licenses) { result.group_by(&:category) } let(:categorised_licenses) { result.group_by(&:category) }
...@@ -31,7 +31,7 @@ describe LicenseTemplateFinder do ...@@ -31,7 +31,7 @@ describe LicenseTemplateFinder do
it 'returns all licenses known by the Licensee gem' do it 'returns all licenses known by the Licensee gem' do
from_licensee = Licensee::License.all.map { |l| l.key } from_licensee = Licensee::License.all.map { |l| l.key }
expect(result.map(&:id)).to match_array(from_licensee) expect(result.map(&:key)).to match_array(from_licensee)
end end
it 'correctly copies all attributes' do it 'correctly copies all attributes' do
......
...@@ -4,6 +4,8 @@ describe TemplateFinder do ...@@ -4,6 +4,8 @@ describe TemplateFinder do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
describe '#build' do describe '#build' do
let(:project) { build_stubbed(:project) }
where(:type, :expected_class) do where(:type, :expected_class) do
:dockerfiles | described_class :dockerfiles | described_class
:gitignores | described_class :gitignores | described_class
...@@ -12,9 +14,10 @@ describe TemplateFinder do ...@@ -12,9 +14,10 @@ describe TemplateFinder do
end end
with_them do with_them do
subject { described_class.build(type) } subject(:finder) { described_class.build(type, project) }
it { is_expected.to be_a(expected_class) } it { is_expected.to be_a(expected_class) }
it { expect(finder.project).to eq(project) }
end end
end end
...@@ -27,19 +30,19 @@ describe TemplateFinder do ...@@ -27,19 +30,19 @@ describe TemplateFinder do
with_them do with_them do
it 'returns all vendored templates when no name is specified' do it 'returns all vendored templates when no name is specified' do
result = described_class.new(type).execute result = described_class.new(type, nil).execute
expect(result).to include(have_attributes(name: vendored_name)) expect(result).to include(have_attributes(name: vendored_name))
end end
it 'returns only the specified vendored template when a name is specified' do it 'returns only the specified vendored template when a name is specified' do
result = described_class.new(type, name: vendored_name).execute result = described_class.new(type, nil, name: vendored_name).execute
expect(result).to have_attributes(name: vendored_name) expect(result).to have_attributes(name: vendored_name)
end end
it 'returns nil when an unknown name is specified' do it 'returns nil when an unknown name is specified' do
result = described_class.new(type, name: 'unknown').execute result = described_class.new(type, nil, name: 'unknown').execute
expect(result).to be_nil expect(result).to be_nil
end end
......
...@@ -54,6 +54,6 @@ describe LicenseTemplate do ...@@ -54,6 +54,6 @@ describe LicenseTemplate do
end end
def build_template(content) def build_template(content)
described_class.new(id: 'foo', name: 'foo', category: :Other, content: content) described_class.new(key: 'foo', name: 'foo', category: :Other, content: content)
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