Commit 9a50df2d authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '241166-sse-config-mounts' into 'master'

Introduce configuration value for mounts

See merge request gitlab-org/gitlab!43485
parents 78e23f53 2658f557
...@@ -12,10 +12,19 @@ const initStaticSiteEditor = el => { ...@@ -12,10 +12,19 @@ const initStaticSiteEditor = el => {
namespace, namespace,
project, project,
mergeRequestsIllustrationPath, mergeRequestsIllustrationPath,
// NOTE: The following variables are not yet used, but are supported by the config file,
// so we are adding them here as a convenience for future use.
// eslint-disable-next-line no-unused-vars
staticSiteGenerator,
// eslint-disable-next-line no-unused-vars
imageUploadPath,
mounts,
} = el.dataset; } = el.dataset;
// NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object.
// eslint-disable-next-line no-unused-vars
const mountsObject = JSON.parse(mounts);
const { current_username: username } = window.gon; const { current_username: username } = window.gon;
const returnUrl = el.dataset.returnUrl || null; const returnUrl = el.dataset.returnUrl || null;
const router = createRouter(baseUrl); const router = createRouter(baseUrl);
const apolloProvider = createApolloProvider({ const apolloProvider = createApolloProvider({
isSupportedContent: parseBoolean(isSupportedContent), isSupportedContent: parseBoolean(isSupportedContent),
......
...@@ -25,7 +25,7 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController ...@@ -25,7 +25,7 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
).execute ).execute
if service_response.success? if service_response.success?
@data = service_response.payload @data = serialize_necessary_payload_values_to_json(service_response.payload)
else else
# TODO: For now, if the service returns any error, the user is redirected # TODO: For now, if the service returns any error, the user is redirected
# to the root project page with the error message displayed as an alert. # to the root project page with the error message displayed as an alert.
...@@ -38,6 +38,17 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController ...@@ -38,6 +38,17 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
private private
def serialize_necessary_payload_values_to_json(payload)
# This will convert booleans, Array-like and Hash-like objects to JSON
payload.transform_values do |value|
if value.is_a?(String) || value.is_a?(Integer)
value
else
value.to_json
end
end
end
def assign_ref_and_path def assign_ref_and_path
@ref, @path = extract_ref(params.fetch(:id)) @ref, @path = extract_ref(params.fetch(:id))
......
---
title: Introduce 'mounts' entry support for '.gitlab/static-site-editor.yml' config file.
merge_request: 43485
author:
type: added
...@@ -15,6 +15,7 @@ module Gitlab ...@@ -15,6 +15,7 @@ module Gitlab
ALLOWED_KEYS = %i[ ALLOWED_KEYS = %i[
image_upload_path image_upload_path
mounts
static_site_generator static_site_generator
].freeze ].freeze
...@@ -26,6 +27,8 @@ module Gitlab ...@@ -26,6 +27,8 @@ module Gitlab
entry :image_upload_path, Entry::ImageUploadPath, entry :image_upload_path, Entry::ImageUploadPath,
description: 'Configuration of the Static Site Editor image upload path.' description: 'Configuration of the Static Site Editor image upload path.'
entry :mounts, Entry::Mounts,
description: 'Configuration of the Static Site Editor mounts.'
entry :static_site_generator, Entry::StaticSiteGenerator, entry :static_site_generator, Entry::StaticSiteGenerator,
description: 'Configuration of the Static Site Editor static site generator.' description: 'Configuration of the Static Site Editor static site generator.'
end end
......
# frozen_string_literal: true
module Gitlab
module StaticSiteEditor
module Config
class FileConfig
module Entry
##
# Entry that represents the mappings of mounted source directories to target paths
#
class Mount < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
ALLOWED_KEYS = %i[source target].freeze
attributes ALLOWED_KEYS
validations do
validates :config, allowed_keys: ALLOWED_KEYS
validates :source, type: String, presence: true
validates :target, type: String, presence: true, allow_blank: true
end
def self.default
# NOTE: This is the default for middleman projects. Ideally, this would be determined
# based on the defaults for whatever `static_site_generator` is configured.
{
source: 'source',
target: ''
}
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module StaticSiteEditor
module Config
class FileConfig
module Entry
##
# Entry that represents the mappings of mounted source directories to target paths
#
class Mounts < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Configurable
include ::Gitlab::Config::Entry::Validatable
entry :mount, Entry::Mount, description: 'Configuration of a Static Site Editor mount.'
validations do
validates :config, type: Array, presence: true
end
def skip_config_hash_validation?
true
end
def self.default
[Entry::Mount.default]
end
end
end
end
end
end
end
...@@ -21,7 +21,7 @@ module Gitlab ...@@ -21,7 +21,7 @@ module Gitlab
project: project.path, project: project.path,
namespace: project.namespace.full_path, namespace: project.namespace.full_path,
return_url: sanitize_url(return_url), return_url: sanitize_url(return_url),
is_supported_content: supported_content?.to_s, is_supported_content: supported_content?,
base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path), base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path),
merge_requests_illustration_path: merge_requests_illustration_path merge_requests_illustration_path: merge_requests_illustration_path
} }
......
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::StaticSiteEditorController do RSpec.describe Projects::StaticSiteEditorController do
let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:data) { instance_double(Hash) } let(:data) { { key: 'value' } }
describe 'GET show' do describe 'GET show' do
render_views render_views
...@@ -52,42 +52,78 @@ RSpec.describe Projects::StaticSiteEditorController do ...@@ -52,42 +52,78 @@ RSpec.describe Projects::StaticSiteEditorController do
end end
end end
%w[developer maintainer].each do |role| context "as developer" do
context "as #{role}" do before do
before_all do project.add_role(user, 'developer')
project.add_role(user, role) sign_in(user)
get :show, params: default_params
end
it 'renders the edit page' do
expect(response).to render_template(:show)
end
it 'assigns ref and path variables' do
expect(assigns(:ref)).to eq('master')
expect(assigns(:path)).to eq('README.md')
end
context 'when combination of ref and path is incorrect' do
let(:default_params) { super().merge(id: 'unknown') }
it 'responds with 404 page' do
expect(response).to have_gitlab_http_status(:not_found)
end end
end
context 'when invalid config file' do
let(:service_response) { ServiceResponse.error(message: 'invalid') }
before do it 'redirects to project page and flashes error message' do
sign_in(user) expect(response).to redirect_to(project_path(project))
get :show, params: default_params expect(response).to set_flash[:alert].to('invalid')
end end
end
it 'renders the edit page' do context 'with a service response payload containing multiple data types' do
expect(response).to render_template(:show) let(:data) do
{
a_string: 'string',
an_array: [
{
foo: 'bar'
}
],
an_integer: 123,
a_hash: {
a_deeper_hash: {
foo: 'bar'
}
},
a_boolean: true
}
end end
it 'assigns a required variables' do let(:assigns_data) { assigns(:data) }
expect(assigns(:data)).to eq(data)
expect(assigns(:ref)).to eq('master') it 'leaves data values which are strings as strings' do
expect(assigns(:path)).to eq('README.md') expect(assigns_data[:a_string]).to eq('string')
end end
context 'when combination of ref and path is incorrect' do it 'leaves data values which are integers as integers' do
let(:default_params) { super().merge(id: 'unknown') } expect(assigns_data[:an_integer]).to eq(123)
end
it 'responds with 404 page' do it 'serializes data values which are booleans to JSON' do
expect(response).to have_gitlab_http_status(:not_found) expect(assigns_data[:a_boolean]).to eq('true')
end
end end
context 'when invalid config file' do it 'serializes data values which are arrays to JSON' do
let(:service_response) { ServiceResponse.error(message: 'invalid') } expect(assigns_data[:an_array]).to eq('[{"foo":"bar"}]')
end
it 'redirects to project page and flashes error message' do it 'serializes data values which are hashes to JSON' do
expect(response).to redirect_to(project_path(project)) expect(assigns_data[:a_hash]).to eq('{"a_deeper_hash":{"foo":"bar"}}')
expect(response).to set_flash[:alert].to('invalid')
end
end end
end end
end end
......
...@@ -21,7 +21,7 @@ RSpec.describe 'Static Site Editor' do ...@@ -21,7 +21,7 @@ RSpec.describe 'Static Site Editor' do
visit sse_path visit sse_path
end end
it 'renders Static Site Editor page with all generated config values and default config file values' do it 'renders SSE page with all generated config values and default config file values' do
node = page.find('#static-site-editor') node = page.find('#static-site-editor')
# assert generated config values are present # assert generated config values are present
...@@ -29,23 +29,30 @@ RSpec.describe 'Static Site Editor' do ...@@ -29,23 +29,30 @@ RSpec.describe 'Static Site Editor' do
expect(node['data-branch']).to eq('master') expect(node['data-branch']).to eq('master')
expect(node['data-commit-id']).to match(/\A[0-9a-f]{40}\z/) expect(node['data-commit-id']).to match(/\A[0-9a-f]{40}\z/)
expect(node['data-is-supported-content']).to eq('true') expect(node['data-is-supported-content']).to eq('true')
expect(node['data-merge-requests-illustration-path']).to match(%r{/assets/illustrations/merge_requests-.*\.svg}) expect(node['data-merge-requests-illustration-path'])
.to match(%r{/assets/illustrations/merge_requests-.*\.svg})
expect(node['data-namespace']).to eq(project.namespace.full_path) expect(node['data-namespace']).to eq(project.namespace.full_path)
expect(node['data-project']).to eq(project.path) expect(node['data-project']).to eq(project.path)
expect(node['data-project-id']).to eq(project.id.to_s) expect(node['data-project-id']).to eq(project.id.to_s)
# assert default config file values are present # assert default config file values are present
expect(node['data-image-upload-path']).to eq('source/images') expect(node['data-image-upload-path']).to eq('source/images')
expect(node['data-mounts']).to eq('[{"source":"source","target":""}]')
expect(node['data-static-site-generator']).to eq('middleman') expect(node['data-static-site-generator']).to eq('middleman')
end end
end end
context "when a config file is present" do context "when a config file is present" do
let(:config_file_yml) do let(:config_file_yml) do
<<-EOS <<~YAML
image_upload_path: custom-image-upload-path image_upload_path: custom-image-upload-path
mounts:
- source: source1
target: ""
- source: source2
target: target2
static_site_generator: middleman static_site_generator: middleman
EOS YAML
end end
before do before do
...@@ -60,7 +67,9 @@ RSpec.describe 'Static Site Editor' do ...@@ -60,7 +67,9 @@ RSpec.describe 'Static Site Editor' do
node = page.find('#static-site-editor') node = page.find('#static-site-editor')
# assert user-specified config file values are present # assert user-specified config file values are present
expected_mounts = '[{"source":"source1","target":""},{"source":"source2","target":"target2"}]'
expect(node['data-image-upload-path']).to eq('custom-image-upload-path') expect(node['data-image-upload-path']).to eq('custom-image-upload-path')
expect(node['data-mounts']).to eq(expected_mounts)
expect(node['data-static-site-generator']).to eq('middleman') expect(node['data-static-site-generator']).to eq('middleman')
end end
end end
......
...@@ -4,9 +4,19 @@ require 'spec_helper' ...@@ -4,9 +4,19 @@ require 'spec_helper'
RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
let(:global) { described_class.new(hash) } let(:global) { described_class.new(hash) }
let(:default_static_site_generator_value) { 'middleman' }
let(:default_image_upload_path_value) { 'source/images' } let(:default_image_upload_path_value) { 'source/images' }
let(:default_mounts_value) do
[
{
source: 'source',
target: ''
}
]
end
let(:default_static_site_generator_value) { 'middleman' }
shared_examples_for 'valid default configuration' do shared_examples_for 'valid default configuration' do
describe '#compose!' do describe '#compose!' do
before do before do
...@@ -18,7 +28,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do ...@@ -18,7 +28,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
end end
it 'creates node object for each entry' do it 'creates node object for each entry' do
expect(global.descendants.count).to eq 2 expect(global.descendants.count).to eq 3
end end
it 'creates node object using valid class' do it 'creates node object using valid class' do
...@@ -67,6 +77,12 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do ...@@ -67,6 +77,12 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
end end
end end
describe '#mounts_value' do
it 'returns correct values' do
expect(global.mounts_value).to eq(default_mounts_value)
end
end
describe '#static_site_generator_value' do describe '#static_site_generator_value' do
it 'returns correct values' do it 'returns correct values' do
expect(global.static_site_generator_value).to eq(default_static_site_generator_value) expect(global.static_site_generator_value).to eq(default_static_site_generator_value)
...@@ -84,6 +100,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do ...@@ -84,6 +100,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
it 'contains the expected node names' do it 'contains the expected node names' do
expected_node_names = %i[ expected_node_names = %i[
image_upload_path image_upload_path
mounts
static_site_generator static_site_generator
] ]
expect(described_class.nodes.keys).to match_array(expected_node_names) expect(described_class.nodes.keys).to match_array(expected_node_names)
...@@ -96,6 +113,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do ...@@ -96,6 +113,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
let(:expected_node_object_classes) do let(:expected_node_object_classes) do
[ [
Gitlab::StaticSiteEditor::Config::FileConfig::Entry::ImageUploadPath, Gitlab::StaticSiteEditor::Config::FileConfig::Entry::ImageUploadPath,
Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Mounts,
Gitlab::StaticSiteEditor::Config::FileConfig::Entry::StaticSiteGenerator Gitlab::StaticSiteEditor::Config::FileConfig::Entry::StaticSiteGenerator
] ]
end end
...@@ -103,6 +121,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do ...@@ -103,6 +121,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
let(:hash) do let(:hash) do
{ {
image_upload_path: default_image_upload_path_value, image_upload_path: default_image_upload_path_value,
mounts: default_mounts_value,
static_site_generator: default_static_site_generator_value static_site_generator: default_static_site_generator_value
} }
end end
...@@ -114,6 +133,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do ...@@ -114,6 +133,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
context 'when value is an empty hash' do context 'when value is an empty hash' do
let(:expected_node_object_classes) do let(:expected_node_object_classes) do
[ [
Gitlab::Config::Entry::Unspecified,
Gitlab::Config::Entry::Unspecified, Gitlab::Config::Entry::Unspecified,
Gitlab::Config::Entry::Unspecified Gitlab::Config::Entry::Unspecified
] ]
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Mount do
subject(:entry) { described_class.new(config) }
describe 'validations' do
context 'with a valid config' do
context 'and target is a non-empty string' do
let(:config) do
{
source: 'source',
target: 'sub-site'
}
end
it { is_expected.to be_valid }
describe '#value' do
it 'returns mount configuration' do
expect(entry.value).to eq config
end
end
end
context 'and target is an empty string' do
let(:config) do
{
source: 'source',
target: ''
}
end
it { is_expected.to be_valid }
describe '#value' do
it 'returns mount configuration' do
expect(entry.value).to eq config
end
end
end
end
context 'with an invalid config' do
context 'when source is not a string' do
let(:config) { { source: 123, target: 'target' } }
it { is_expected.not_to be_valid }
it 'reports error' do
expect(entry.errors)
.to include 'mount source should be a string'
end
end
context 'when source is not present' do
let(:config) { { target: 'target' } }
it { is_expected.not_to be_valid }
it 'reports error' do
expect(entry.errors)
.to include "mount source can't be blank"
end
end
context 'when target is not a string' do
let(:config) { { source: 'source', target: 123 } }
it { is_expected.not_to be_valid }
it 'reports error' do
expect(entry.errors)
.to include 'mount target should be a string'
end
end
context 'when there is an unknown key present' do
let(:config) { { test: 100 } }
it { is_expected.not_to be_valid }
it 'reports error' do
expect(entry.errors)
.to include 'mount config contains unknown keys: test'
end
end
end
end
describe '.default' do
it 'returns default mount' do
expect(described_class.default)
.to eq({
source: 'source',
target: ''
})
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Mounts do
subject(:entry) { described_class.new(config) }
describe 'validations' do
context 'with a valid config' do
let(:config) do
[
{
source: 'source',
target: ''
},
{
source: 'sub-site/source',
target: 'sub-site'
}
]
end
it { is_expected.to be_valid }
describe '#value' do
it 'returns mounts configuration' do
expect(entry.value).to eq config
end
end
end
context 'with an invalid config' do
let(:config) { { not_an_array: true } }
it { is_expected.not_to be_valid }
it 'reports errors about wrong type' do
expect(entry.errors)
.to include 'mounts config should be a array'
end
end
end
describe '.default' do
it 'returns default mounts' do
expect(described_class.default)
.to eq([{
source: 'source',
target: ''
}])
end
end
end
...@@ -29,7 +29,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do ...@@ -29,7 +29,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
project: 'project', project: 'project',
project_id: project.id, project_id: project.id,
return_url: 'http://example.com', return_url: 'http://example.com',
is_supported_content: 'true', is_supported_content: true,
base_url: '/namespace/project/-/sse/master%2FREADME.md', base_url: '/namespace/project/-/sse/master%2FREADME.md',
merge_requests_illustration_path: %r{illustrations/merge_requests} merge_requests_illustration_path: %r{illustrations/merge_requests}
}) })
...@@ -65,7 +65,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do ...@@ -65,7 +65,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
stub_feature_flags(sse_erb_support: project) stub_feature_flags(sse_erb_support: project)
end end
it { is_expected.to include(is_supported_content: 'true') } it { is_expected.to include(is_supported_content: true) }
end end
context 'when feature flag is disabled' do context 'when feature flag is disabled' do
...@@ -75,7 +75,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do ...@@ -75,7 +75,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
stub_feature_flags(sse_erb_support: false) stub_feature_flags(sse_erb_support: false)
end end
it { is_expected.to include(is_supported_content: 'false') } it { is_expected.to include(is_supported_content: false) }
end end
end end
...@@ -88,31 +88,31 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do ...@@ -88,31 +88,31 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
context 'when branch is not master' do context 'when branch is not master' do
let(:ref) { 'my-branch' } let(:ref) { 'my-branch' }
it { is_expected.to include(is_supported_content: 'false') } it { is_expected.to include(is_supported_content: false) }
end end
context 'when file does not have a markdown extension' do context 'when file does not have a markdown extension' do
let(:path) { 'README.txt' } let(:path) { 'README.txt' }
it { is_expected.to include(is_supported_content: 'false') } it { is_expected.to include(is_supported_content: false) }
end end
context 'when file does not have an extension' do context 'when file does not have an extension' do
let(:path) { 'README' } let(:path) { 'README' }
it { is_expected.to include(is_supported_content: 'false') } it { is_expected.to include(is_supported_content: false) }
end end
context 'when file does not exist' do context 'when file does not exist' do
let(:path) { 'UNKNOWN.md' } let(:path) { 'UNKNOWN.md' }
it { is_expected.to include(is_supported_content: 'false') } it { is_expected.to include(is_supported_content: false) }
end end
context 'when repository is empty' do context 'when repository is empty' do
let(:repository) { create(:project_empty_repo).repository } let(:repository) { create(:project_empty_repo).repository }
it { is_expected.to include(is_supported_content: 'false') } it { is_expected.to include(is_supported_content: false) }
end end
context 'when return_url is not a valid URL' do context 'when return_url is not a valid URL' do
......
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