Commit 3f827ec7 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'ajk-gl-identifier' into 'master'

Refactor identifier parsing

See merge request gitlab-org/gitlab!34638
parents 1c414a88 6afa879a
...@@ -43,10 +43,10 @@ module Gitlab ...@@ -43,10 +43,10 @@ module Gitlab
end end
def self.parse(gl_repository) def self.parse(gl_repository)
result = ::Gitlab::GlRepository::Identifier.new(gl_repository) identifier = ::Gitlab::GlRepository::Identifier.parse(gl_repository)
repo_type = result.repo_type repo_type = identifier.repo_type
container = result.fetch_container! container = identifier.container
[container, repo_type.project_for(container), repo_type] [container, repo_type.project_for(container), repo_type]
end end
......
...@@ -3,71 +3,83 @@ ...@@ -3,71 +3,83 @@
module Gitlab module Gitlab
class GlRepository class GlRepository
class Identifier class Identifier
attr_reader :gl_repository, :repo_type include Gitlab::Utils::StrongMemoize
def initialize(gl_repository) InvalidIdentifier = Class.new(ArgumentError)
@gl_repository = gl_repository
@segments = gl_repository.split('-')
raise_error if segments.size > 3 def self.parse(gl_repository)
segments = gl_repository&.split('-')
@repo_type = find_repo_type # gl_repository can either have 2 or 3 segments:
@container_id = find_container_id #
@container_class = find_container_class # TODO: convert all 2-segment format to 3-segment:
# https://gitlab.com/gitlab-org/gitlab/-/issues/219192
identifier = case segments&.size
when 2
TwoPartIdentifier.new(*segments)
when 3
ThreePartIdentifier.new(*segments)
end end
def fetch_container! return identifier if identifier&.valid?
container_class.find_by_id(container_id)
end
private raise InvalidIdentifier, %Q(Invalid GL Repository "#{gl_repository}")
end
attr_reader :segments, :container_class, :container_id # The older 2-segment format, where the container is implied.
# eg. project-1, wiki-1
class TwoPartIdentifier < Identifier
def initialize(repo_type_name, container_id_str)
@container_id_str = container_id_str
@repo_type_name = repo_type_name
end
def find_repo_type private
type_name = three_segments_format? ? segments.last : segments.first
type = Gitlab::GlRepository.types[type_name]
raise_error unless type def container_class
repo_type.container_class
end
end
type # The newer 3-segment format, where the container is explicit
# eg. group-1-wiki, project-1-wiki
class ThreePartIdentifier < Identifier
def initialize(container_type, container_id_str, repo_type_name)
@container_id_str = container_id_str
@container_type = container_type
@repo_type_name = repo_type_name
end end
def find_container_class private
if three_segments_format?
case segments[0] def container_class
case @container_type
when 'project' when 'project'
Project Project
when 'group' when 'group'
Group Group
else
raise_error
end end
else
repo_type.container_class
end end
end end
def find_container_id def repo_type
id = Integer(segments[1], 10, exception: false) strong_memoize(:repo_type) { Gitlab::GlRepository.types[repo_type_name] }
end
raise_error unless id
id def container
strong_memoize(:container) { container_class.find_by_id(container_id) }
end end
# gl_repository can either have 2 or 3 segments: def valid?
# "wiki-1" is the older 2-segment format, where container is implied. repo_type.present? && container_class.present? && container_id&.positive?
# "group-1-wiki" is the newer 3-segment format, including container information.
#
# TODO: convert all 2-segment format to 3-segment:
# https://gitlab.com/gitlab-org/gitlab/-/issues/219192
def three_segments_format?
segments.size == 3
end end
def raise_error private
raise ArgumentError, "Invalid GL Repository \"#{gl_repository}\""
attr_reader :container_id_str, :repo_type_name
def container_id
strong_memoize(:container_id) { Integer(container_id_str, 10, exception: false) }
end end
end end
end end
......
...@@ -14,6 +14,21 @@ RSpec.describe Gitlab::GlRepository::Identifier do ...@@ -14,6 +14,21 @@ RSpec.describe Gitlab::GlRepository::Identifier do
let(:expected_container) { project } let(:expected_container) { project }
let(:expected_type) { Gitlab::GlRepository::PROJECT } let(:expected_type) { Gitlab::GlRepository::PROJECT }
end end
pending 'https://gitlab.com/gitlab-org/gitlab/-/issues/219192' do
it_behaves_like 'parsing gl_repository identifier' do
let(:record_id) { project.id }
let(:identifier) { "project-#{record_id}-code" }
let(:expected_container) { project }
let(:expected_type) { Gitlab::GlRepository::PROJECT }
end
end
it_behaves_like 'parsing gl_repository identifier' do
let(:identifier) { "project-1000000" }
let(:expected_container) { nil }
let(:expected_type) { Gitlab::GlRepository::PROJECT }
end
end end
describe 'wiki' do describe 'wiki' do
...@@ -23,6 +38,13 @@ RSpec.describe Gitlab::GlRepository::Identifier do ...@@ -23,6 +38,13 @@ RSpec.describe Gitlab::GlRepository::Identifier do
let(:expected_container) { project } let(:expected_container) { project }
let(:expected_type) { Gitlab::GlRepository::WIKI } let(:expected_type) { Gitlab::GlRepository::WIKI }
end end
it_behaves_like 'parsing gl_repository identifier' do
let(:record_id) { project.id }
let(:identifier) { "project-#{record_id}-wiki" }
let(:expected_container) { project }
let(:expected_type) { Gitlab::GlRepository::WIKI }
end
end end
describe 'snippet' do describe 'snippet' do
...@@ -54,29 +76,30 @@ RSpec.describe Gitlab::GlRepository::Identifier do ...@@ -54,29 +76,30 @@ RSpec.describe Gitlab::GlRepository::Identifier do
end end
end end
describe 'incorrect format' do context 'when the format is incorrect' do
def expect_error_raised_for(identifier) where(:identifier) do
expect { described_class.new(identifier) }.to raise_error(ArgumentError) [
'wiki-noid',
'foo-2',
'project-0',
'2-project',
'snippet-2-wiki',
'project-wibble-wiki',
'wiki-1-project',
'snippet',
'project-1-wiki-bar'
]
end end
it 'raises error for incorrect id' do with_them do
expect_error_raised_for('wiki-noid') it 'raises InvalidIdentifier' do
expect { described_class.parse(identifier) }.to raise_error(described_class::InvalidIdentifier)
end end
it 'raises error for incorrect type' do
expect_error_raised_for('foo-2')
end
it 'raises error for incorrect three-segment container' do
expect_error_raised_for('snippet-2-wiki')
end
it 'raises error for one segment' do
expect_error_raised_for('snippet')
end end
it 'raises error for more than three segments' do it 'raises InvalidIdentifier on project-1-project' do
expect_error_raised_for('project-1-wiki-bar') pending 'https://gitlab.com/gitlab-org/gitlab/-/issues/219192'
expect { described_class.parse('project-1-project') }.to raise_error(described_class::InvalidIdentifier)
end end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'parsing gl_repository identifier' do RSpec.shared_examples 'parsing gl_repository identifier' do
subject { described_class.new(identifier) } subject { described_class.parse(identifier) }
it 'returns correct information' do it 'returns correct information' do
aggregate_failures do expect(subject).to have_attributes(
expect(subject.repo_type).to eq(expected_type) repo_type: expected_type,
expect(subject.fetch_container!).to eq(expected_container) container: expected_container
end )
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