foreman_templates 9.0.0 → 9.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/template_controller.rb +3 -3
- data/app/controllers/ui_template_syncs_controller.rb +9 -3
- data/app/services/foreman_templates/template_exporter.rb +1 -1
- data/app/services/foreman_templates/template_importer.rb +1 -1
- data/app/views/template_sync_settings/show.json.rabl +2 -2
- data/app/views/template_syncs/index.html.erb +14 -17
- data/app/views/ui_template_syncs/template_attrs.json.rabl +1 -1
- data/db/migrate/20211122154929_templates_settings_category_to_dsl.rb +5 -0
- data/lib/foreman_templates/engine.rb +76 -10
- data/lib/foreman_templates/version.rb +1 -1
- data/lib/foreman_templates.rb +15 -0
- data/lib/tasks/foreman_templates_tasks.rake +10 -9
- data/webpack/ForemanTemplates.js +17 -13
- data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +2 -0
- data/webpack/__mocks__/foremanReact/components/Pagination/index.js +2 -0
- data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncForm.js +95 -140
- data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormHelpers.js +43 -0
- data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/index.js +0 -4
- data/webpack/components/TemplateSyncResult/TemplateSyncResultHelpers.js +2 -2
- data/webpack/components/TemplateSyncResult/TemplateSyncResultReducer.js +1 -1
- data/webpack/components/TemplateSyncResult/__tests__/__snapshots__/TemplateSyncResultReducer.test.js.snap +2 -2
- data/webpack/components/TemplateSyncResult/components/SyncResultList.js +11 -6
- data/webpack/components/TemplateSyncResult/components/__tests__/SyncResultList.test.js +1 -1
- data/webpack/components/TemplateSyncResult/components/__tests__/__snapshots__/SyncResultList.test.js.snap +3 -9
- metadata +7 -5
- data/app/models/setting/template_sync.rb +0 -115
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bda8e472a4ae76b0f1480738e9059423a249c4156842deebc385baa2f3c2a5ca
|
4
|
+
data.tar.gz: bb04b38ce6791e62f90fcea5a177d38ca62402e6c3037e7f3ed85c416f91696c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c35d3ca52df290a50a3e1788ea654a0588067ab02278a5b77829bfaec46d8c855ae36f554a608eaf8e9c5797b650d5be1a95ed4e7f8486c905e601068180fc5
|
7
|
+
data.tar.gz: b58a200b8374f22481e1397949cc4126315234b6238ce08895acedb628c826515ce9ab514b85942b8388350f0e8939b0d088ccc6641fc4b3b84a1fc65ef16961
|
@@ -17,9 +17,9 @@ module Api
|
|
17
17
|
|
18
18
|
api :POST, "/templates/import/", N_("Initiate Import")
|
19
19
|
param :prefix, String, :required => false, :desc => N_("The string all imported templates should begin with.")
|
20
|
-
param :associate,
|
20
|
+
param :associate, ForemanTemplates.associate_types.keys, :required => false, :desc => N_("Associate to OS's, Locations & Organizations. Options are: always, new or never.")
|
21
21
|
param :force, :bool, :required => false, :desc => N_("Update templates that are locked")
|
22
|
-
param :lock,
|
22
|
+
param :lock, ForemanTemplates.lock_types.keys + ["true", "false", "0", "1"], :required => false, :desc => N_("Lock imported templates")
|
23
23
|
param :verbose, :bool, :required => false, :desc => N_("Show template diff in response")
|
24
24
|
param_group :foreman_template_sync_params
|
25
25
|
param_group :taxonomies, ::Api::V2::BaseController
|
@@ -32,7 +32,7 @@ module Api
|
|
32
32
|
end
|
33
33
|
|
34
34
|
api :POST, "/templates/export", N_("Initiate Export")
|
35
|
-
param :metadata_export_mode,
|
35
|
+
param :metadata_export_mode, ForemanTemplates.metadata_export_mode_types.keys, :required => false, :desc => N_("Specify how to handle metadata")
|
36
36
|
param :commit_msg, String, :desc => N_("Custom commit message for templates export")
|
37
37
|
param_group :foreman_template_sync_params
|
38
38
|
param_group :taxonomies, ::Api::V2::BaseController
|
@@ -8,8 +8,8 @@ class UiTemplateSyncsController < ApplicationController
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def sync_settings
|
11
|
-
import_settings =
|
12
|
-
export_settings =
|
11
|
+
import_settings = setting_definitions(ForemanTemplates::IMPORT_SETTING_NAMES)
|
12
|
+
export_settings = setting_definitions(ForemanTemplates::EXPORT_SETTING_NAMES)
|
13
13
|
@results = OpenStruct.new(:import => import_settings, :export => export_settings)
|
14
14
|
end
|
15
15
|
|
@@ -43,6 +43,12 @@ class UiTemplateSyncsController < ApplicationController
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def render_errors(messages, severity = 'danger')
|
46
|
-
render :json => { :error => { :errors => { :base => messages }, :severity => severity } }, :status => :unprocessable_entity
|
46
|
+
render :json => { :error => { :errors => { :base => messages }, full_messages: messages, :severity => severity } }, :status => :unprocessable_entity
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def setting_definitions(short_names)
|
52
|
+
short_names.map { |name| Foreman.settings.find("template_sync_#{name}") }
|
47
53
|
end
|
48
54
|
end
|
@@ -94,7 +94,7 @@ module ForemanTemplates
|
|
94
94
|
def templates_to_dump
|
95
95
|
find_templates.each do |template|
|
96
96
|
if filter.present?
|
97
|
-
exportable =
|
97
|
+
exportable = /#{filter}/i.match?(template.name) ? !negate : negate
|
98
98
|
result = ExportResult.new(template, exportable)
|
99
99
|
next @result_lines << result.matching_filter unless exportable
|
100
100
|
|
@@ -70,7 +70,7 @@ module ForemanTemplates
|
|
70
70
|
@result_lines << parse_result.check_for_errors
|
71
71
|
rescue NameError => e
|
72
72
|
@result_lines << parse_result.name_error(e, metadata['model'])
|
73
|
-
rescue => e
|
73
|
+
rescue StandardError => e
|
74
74
|
@result_lines << parse_result.add_exception(e)
|
75
75
|
end
|
76
76
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
object @setting
|
2
2
|
|
3
3
|
node do |setting|
|
4
|
-
{ :name => setting.
|
4
|
+
{ :name => setting.name.delete_prefix('template_sync_') }
|
5
5
|
end
|
6
6
|
|
7
7
|
attributes :id, :value, :description, :settings_type, :default, :full_name
|
8
8
|
|
9
9
|
node do |setting|
|
10
|
-
{ :selection => setting.
|
10
|
+
{ :selection => (setting.select_values || {}).map { |key, label| { value: key, label: label } } }
|
11
11
|
end
|
@@ -5,20 +5,17 @@
|
|
5
5
|
<%= webpacked_plugins_css_for :foreman_templates %>
|
6
6
|
<% end %>
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
:export => authorized_for(:controller => :ui_template_syncs, :action => :export)
|
23
|
-
}
|
24
|
-
}.to_json )%>
|
8
|
+
<%= react_component('ForemanTemplates',
|
9
|
+
{ :apiUrls => {
|
10
|
+
:exportUrl => export_ui_template_syncs_path,
|
11
|
+
:syncSettingsUrl => sync_settings_ui_template_syncs_path,
|
12
|
+
:importUrl => import_ui_template_syncs_path
|
13
|
+
},
|
14
|
+
:validationData => { :repo => ForemanTemplates::Action.repo_start_with },
|
15
|
+
:editPaths => edit_paths,
|
16
|
+
:fileRepoStartWith => ForemanTemplates::Action.file_repo_start_with,
|
17
|
+
:userPermissions => {
|
18
|
+
:import => authorized_for(:controller => :ui_template_syncs, :action => :import),
|
19
|
+
:export => authorized_for(:controller => :ui_template_syncs, :action => :export)
|
20
|
+
}
|
21
|
+
})%>
|
@@ -11,7 +11,7 @@ node(:class_name) do |template|
|
|
11
11
|
end
|
12
12
|
|
13
13
|
node(:humanized_class_name) do |template|
|
14
|
-
template.class.name.underscore.split('_').map
|
14
|
+
template.class.name.underscore.split('_').map(&:capitalize).join(' ')
|
15
15
|
end
|
16
16
|
|
17
17
|
node(:can_edit) do |template|
|
@@ -10,10 +10,6 @@ module ForemanTemplates
|
|
10
10
|
class Engine < ::Rails::Engine
|
11
11
|
engine_name 'foreman_templates'
|
12
12
|
|
13
|
-
initializer 'foreman_templates.load_default_settings', :before => :load_config_initializers do
|
14
|
-
require_dependency File.expand_path('../../app/models/setting/template_sync.rb', __dir__) if (Setting.table_exists? rescue(false))
|
15
|
-
end
|
16
|
-
|
17
13
|
initializer "foreman_templates.add_rabl_view_path" do
|
18
14
|
Rabl.configure do |config|
|
19
15
|
config.view_paths << ForemanTemplates::Engine.root.join('app', 'views')
|
@@ -28,10 +24,78 @@ module ForemanTemplates
|
|
28
24
|
|
29
25
|
initializer 'foreman_templates.register_plugin', :before => :finisher_hook do
|
30
26
|
Foreman::Plugin.register :foreman_templates do
|
31
|
-
requires_foreman '>=
|
27
|
+
requires_foreman '>= 3.2'
|
32
28
|
|
33
29
|
apipie_documented_controllers ["#{ForemanTemplates::Engine.root}/app/controllers/api/v2/*.rb"]
|
34
30
|
|
31
|
+
settings do
|
32
|
+
category(:template_sync, N_('Template Sync')) do
|
33
|
+
setting('template_sync_verbose',
|
34
|
+
type: :boolean,
|
35
|
+
description: N_('Choose verbosity for Rake task importing templates'),
|
36
|
+
default: false,
|
37
|
+
full_name: N_('Verbosity'))
|
38
|
+
setting('template_sync_associate',
|
39
|
+
type: :string,
|
40
|
+
description: N_('Associate templates to OS, organization and location'),
|
41
|
+
default: 'new',
|
42
|
+
full_name: N_('Associate'),
|
43
|
+
collection: -> { ForemanTemplates.associate_types })
|
44
|
+
setting('template_sync_prefix',
|
45
|
+
type: :string,
|
46
|
+
description: N_('The string that will be added as prefix to imported templates'),
|
47
|
+
default: "",
|
48
|
+
full_name: N_('Prefix'))
|
49
|
+
setting('template_sync_dirname',
|
50
|
+
type: :string,
|
51
|
+
description: N_('The directory within the Git repo containing the templates'),
|
52
|
+
default: '/',
|
53
|
+
full_name: N_('Dirname'))
|
54
|
+
setting('template_sync_filter',
|
55
|
+
type: :string,
|
56
|
+
description: N_('Import/export names matching this regex (case-insensitive; snippets are not filtered)'),
|
57
|
+
default: '',
|
58
|
+
full_name: N_('Filter'))
|
59
|
+
setting('template_sync_repo',
|
60
|
+
type: :string,
|
61
|
+
description: N_('Target path to import/export. Different protocols can be used, for example /tmp/dir, git://example.com, https://example.com, ssh://example.com. When exporting to /tmp, note that production deployments may be configured to use private tmp.'),
|
62
|
+
default: 'https://github.com/theforeman/community-templates.git',
|
63
|
+
full_name: N_('Repo'))
|
64
|
+
setting('template_sync_negate',
|
65
|
+
type: :boolean,
|
66
|
+
description: N_('Negate the filter for import/export'),
|
67
|
+
default: false,
|
68
|
+
full_name: N_('Negate'))
|
69
|
+
setting('template_sync_branch',
|
70
|
+
type: :string,
|
71
|
+
description: N_('Default branch in Git repo'),
|
72
|
+
default: '',
|
73
|
+
full_name: N_('Branch'))
|
74
|
+
setting('template_sync_metadata_export_mode',
|
75
|
+
type: :string,
|
76
|
+
description: N_('Default metadata export mode, refresh re-renders metadata, keep will keep existing metadata, remove exports template without metadata'),
|
77
|
+
default: 'refresh',
|
78
|
+
full_name: N_('Metadata export mode'),
|
79
|
+
collection: -> { ForemanTemplates.metadata_export_mode_types })
|
80
|
+
setting('template_sync_force',
|
81
|
+
type: :boolean,
|
82
|
+
description: N_('Should importing overwrite locked templates?'),
|
83
|
+
default: false,
|
84
|
+
full_name: N_('Force import'))
|
85
|
+
setting('template_sync_lock',
|
86
|
+
type: :string,
|
87
|
+
description: N_('How to handle lock for imported templates?'),
|
88
|
+
default: 'keep',
|
89
|
+
full_name: N_('Lock templates'),
|
90
|
+
collection: -> { ForemanTemplates.lock_types })
|
91
|
+
setting('template_sync_commit_msg',
|
92
|
+
type: :string,
|
93
|
+
description: N_('Custom commit message for templates export'),
|
94
|
+
default: 'Templates export made by a Foreman user',
|
95
|
+
full_name: N_('Commit message'))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
35
99
|
security_block :templates do
|
36
100
|
permission :import_templates, {
|
37
101
|
:"api/v2/template" => [:import],
|
@@ -49,15 +113,17 @@ module ForemanTemplates
|
|
49
113
|
add_all_permissions_to_default_roles
|
50
114
|
|
51
115
|
menu :top_menu, :template_sync,
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
116
|
+
:url_hash => { :controller => :template_syncs, :action => :index },
|
117
|
+
:caption => N_('Sync Templates'),
|
118
|
+
:parent => :hosts_menu,
|
119
|
+
:before => :ptables,
|
120
|
+
:turbolinks => false
|
57
121
|
end
|
58
122
|
end
|
59
123
|
|
60
124
|
config.to_prepare do
|
125
|
+
Setting::NOT_STRIPPED << 'template_sync_prefix'
|
126
|
+
|
61
127
|
Template.include ForemanTemplates::TemplateExtensions
|
62
128
|
end
|
63
129
|
end
|
data/lib/foreman_templates.rb
CHANGED
@@ -1,4 +1,19 @@
|
|
1
1
|
require 'foreman_templates/engine'
|
2
2
|
|
3
3
|
module ForemanTemplates
|
4
|
+
BASE_SETTING_NAMES = %w(repo branch dirname filter negate).freeze
|
5
|
+
IMPORT_SETTING_NAMES = (BASE_SETTING_NAMES | %w(prefix associate force lock)).freeze
|
6
|
+
EXPORT_SETTING_NAMES = (BASE_SETTING_NAMES | %w(metadata_export_mode commit_msg)).freeze
|
7
|
+
|
8
|
+
def self.associate_types
|
9
|
+
{ 'always' => _('Always'), 'new' => _('New'), 'never' => _('Never') }
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.lock_types
|
13
|
+
{ 'lock' => _('Lock'), 'keep_lock_new' => _('Keep, lock new'), 'keep' => _('Keep, do not lock new'), 'unlock' => _('Unlock') }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.metadata_export_mode_types
|
17
|
+
{ 'refresh' => _('Refresh'), 'keep' => _('Keep'), 'remove' => _('Remove') }
|
18
|
+
end
|
4
19
|
end
|
@@ -20,14 +20,14 @@ namespace :templates do
|
|
20
20
|
verbose = ENV['verbose']
|
21
21
|
|
22
22
|
results = ForemanTemplates::TemplateImporter.new({
|
23
|
-
verbose:
|
24
|
-
repo:
|
25
|
-
branch:
|
26
|
-
prefix:
|
27
|
-
dirname:
|
28
|
-
filter:
|
23
|
+
verbose: verbose,
|
24
|
+
repo: ENV['repo'],
|
25
|
+
branch: ENV['branch'],
|
26
|
+
prefix: ENV['prefix'],
|
27
|
+
dirname: ENV['dirname'],
|
28
|
+
filter: ENV['filter'],
|
29
29
|
associate: ENV['associate'],
|
30
|
-
lock:
|
30
|
+
lock: ENV['lock'],
|
31
31
|
}).import!
|
32
32
|
pp(results[:results].map { |result| result.to_h(verbose) })
|
33
33
|
end
|
@@ -58,8 +58,8 @@ namespace :templates do
|
|
58
58
|
# * negate => negate query [false]
|
59
59
|
# * prefix => The string all templates to purge should ( or not ) begin with [Community ]
|
60
60
|
# * verbose => Print extra information during the run [false]
|
61
|
-
negate:
|
62
|
-
prefix:
|
61
|
+
negate: ENV['negate'],
|
62
|
+
prefix: ENV['prefix'],
|
63
63
|
verbose: ENV['verbose'],
|
64
64
|
}).purge!
|
65
65
|
end
|
@@ -89,6 +89,7 @@ namespace :foreman_templates do
|
|
89
89
|
begin
|
90
90
|
require 'rubocop/rake_task'
|
91
91
|
RuboCop::RakeTask.new(:rubocop_foreman_templates) do |task|
|
92
|
+
task.options = ['--config', ForemanTemplates::Engine.root.join('.rubocop.yml').to_s]
|
92
93
|
task.patterns = ["#{ForemanTemplates::Engine.root}/app/**/*.rb",
|
93
94
|
"#{ForemanTemplates::Engine.root}/lib/**/*.rb",
|
94
95
|
"#{ForemanTemplates::Engine.root}/test/**/*.rb"]
|
data/webpack/ForemanTemplates.js
CHANGED
@@ -4,26 +4,30 @@ import PropTypes from 'prop-types';
|
|
4
4
|
|
5
5
|
import Routes from './Routes';
|
6
6
|
|
7
|
-
const ForemanTemplates = ({
|
7
|
+
const ForemanTemplates = ({
|
8
|
+
apiUrls,
|
9
|
+
validationData,
|
10
|
+
fileRepoStartWith,
|
11
|
+
userPermissions,
|
12
|
+
editPaths,
|
13
|
+
}) => (
|
8
14
|
<Router>
|
9
15
|
<Routes
|
10
|
-
apiUrls={
|
11
|
-
validationData={
|
12
|
-
editPaths={
|
13
|
-
fileRepoStartWith={
|
14
|
-
userPermissions={
|
16
|
+
apiUrls={apiUrls}
|
17
|
+
validationData={validationData}
|
18
|
+
editPaths={editPaths}
|
19
|
+
fileRepoStartWith={fileRepoStartWith}
|
20
|
+
userPermissions={userPermissions}
|
15
21
|
/>
|
16
22
|
</Router>
|
17
23
|
);
|
18
24
|
|
19
25
|
ForemanTemplates.propTypes = {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
fileRepoStartWith: PropTypes.array,
|
26
|
-
}).isRequired,
|
26
|
+
apiUrls: PropTypes.object.isRequired,
|
27
|
+
validationData: PropTypes.object.isRequired,
|
28
|
+
editPaths: PropTypes.object.isRequired,
|
29
|
+
userPermissions: PropTypes.object.isRequired,
|
30
|
+
fileRepoStartWith: PropTypes.array.isRequired,
|
27
31
|
};
|
28
32
|
|
29
33
|
export default ForemanTemplates;
|
data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncForm.js
CHANGED
@@ -1,159 +1,116 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useState } from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { compose } from 'redux';
|
4
4
|
|
5
5
|
import ForemanForm from 'foremanReact/components/common/forms/ForemanForm';
|
6
|
-
import
|
6
|
+
import {
|
7
|
+
useForemanLocation,
|
8
|
+
useForemanOrganization,
|
9
|
+
} from 'foremanReact/Root/Context/ForemanContext';
|
10
|
+
|
7
11
|
import SyncSettingsFields from '../SyncSettingFields';
|
8
12
|
import SyncTypeRadios from '../SyncTypeRadios';
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
const
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
', '
|
36
|
-
)}`,
|
37
|
-
repoFormat(validationData.repo)
|
38
|
-
)
|
39
|
-
.required("can't be blank"),
|
40
|
-
};
|
41
|
-
}
|
42
|
-
return memo;
|
43
|
-
},
|
44
|
-
{}
|
13
|
+
import { redirectToResult, syncFormSchema } from './NewTemplateSyncFormHelpers';
|
14
|
+
|
15
|
+
const NewTemplateSyncForm = ({
|
16
|
+
error,
|
17
|
+
submitForm,
|
18
|
+
importSettings,
|
19
|
+
exportSettings,
|
20
|
+
history,
|
21
|
+
validationData,
|
22
|
+
importUrl,
|
23
|
+
exportUrl,
|
24
|
+
initialValues,
|
25
|
+
userPermissions,
|
26
|
+
}) => {
|
27
|
+
const allowedSyncType = (currentUserPermissions, radioAttrs) =>
|
28
|
+
currentUserPermissions[radioAttrs.permission];
|
29
|
+
|
30
|
+
const radioButtons = [
|
31
|
+
{ label: 'Import', value: 'import', permission: 'import' },
|
32
|
+
{ label: 'Export', value: 'export', permission: 'export' },
|
33
|
+
];
|
34
|
+
|
35
|
+
const [syncType, setSyncType] = useState(
|
36
|
+
radioButtons.find(radioAttrs =>
|
37
|
+
allowedSyncType(userPermissions, radioAttrs)
|
38
|
+
).value
|
45
39
|
);
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
});
|
50
|
-
};
|
51
|
-
|
52
|
-
class NewTemplateSyncForm extends React.Component {
|
53
|
-
allowedSyncType = (userPermissions, radioAttrs) =>
|
54
|
-
this.props.userPermissions[radioAttrs.permission];
|
55
|
-
|
56
|
-
constructor(props) {
|
57
|
-
super(props);
|
58
|
-
|
59
|
-
this.radioButtons = [
|
60
|
-
{ label: 'Import', value: 'import', permission: 'import' },
|
61
|
-
{ label: 'Export', value: 'export', permission: 'export' },
|
62
|
-
];
|
63
|
-
|
64
|
-
this.state = {
|
65
|
-
syncType: this.radioButtons.find(radioAttrs =>
|
66
|
-
this.allowedSyncType(props.userPermissions, radioAttrs)
|
67
|
-
).value,
|
68
|
-
};
|
69
|
-
}
|
70
|
-
|
71
|
-
updateSyncType = event => {
|
72
|
-
this.setState({ syncType: event.target.value });
|
41
|
+
const updateSyncType = event => {
|
42
|
+
setSyncType(event.target.value);
|
73
43
|
};
|
74
44
|
|
75
|
-
permitRadioButtons = buttons =>
|
45
|
+
const permitRadioButtons = buttons =>
|
76
46
|
buttons.filter(buttonAttrs =>
|
77
|
-
|
47
|
+
allowedSyncType(userPermissions, buttonAttrs)
|
78
48
|
);
|
79
49
|
|
80
|
-
initRadioButtons =
|
81
|
-
|
50
|
+
const initRadioButtons = templateSyncType =>
|
51
|
+
permitRadioButtons(radioButtons).map(buttonAttrs => ({
|
82
52
|
get checked() {
|
83
|
-
return
|
53
|
+
return buttonAttrs.value === templateSyncType;
|
84
54
|
},
|
85
|
-
onChange:
|
55
|
+
onChange: updateSyncType,
|
86
56
|
...buttonAttrs,
|
87
57
|
}));
|
88
58
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
controlLabel="Action type"
|
145
|
-
radios={this.initRadioButtons(this.state.syncType)}
|
146
|
-
/>
|
147
|
-
<SyncSettingsFields
|
148
|
-
importSettings={importSettings}
|
149
|
-
exportSettings={exportSettings}
|
150
|
-
syncType={this.state.syncType}
|
151
|
-
resetField={resetToDefault}
|
152
|
-
/>
|
153
|
-
</ForemanForm>
|
154
|
-
);
|
155
|
-
}
|
156
|
-
}
|
59
|
+
const addTaxParams = (key, currentTax) => params => {
|
60
|
+
if (currentTax && currentTax.id) {
|
61
|
+
return { ...params, [key]: [currentTax.id] };
|
62
|
+
}
|
63
|
+
return params;
|
64
|
+
};
|
65
|
+
|
66
|
+
const addOrgParams = addTaxParams(
|
67
|
+
'organization_ids',
|
68
|
+
useForemanOrganization()
|
69
|
+
);
|
70
|
+
const addLocParams = addTaxParams('location_ids', useForemanLocation());
|
71
|
+
|
72
|
+
const resetToDefault = (fieldName, fieldValue) => resetFn =>
|
73
|
+
resetFn(fieldName, fieldValue);
|
74
|
+
|
75
|
+
const handleSubmit = (values, actions) => {
|
76
|
+
const url = syncType === 'import' ? importUrl : exportUrl;
|
77
|
+
return submitForm({
|
78
|
+
url,
|
79
|
+
values: compose(addLocParams, addOrgParams)(values[syncType]),
|
80
|
+
message: `Templates were ${syncType}ed.`,
|
81
|
+
item: 'TemplateSync',
|
82
|
+
actions,
|
83
|
+
successCallback: () =>
|
84
|
+
history.replace({ pathname: '/template_syncs/result' }),
|
85
|
+
});
|
86
|
+
};
|
87
|
+
|
88
|
+
return (
|
89
|
+
<ForemanForm
|
90
|
+
onSubmit={handleSubmit}
|
91
|
+
initialValues={initialValues}
|
92
|
+
validationSchema={syncFormSchema(
|
93
|
+
syncType,
|
94
|
+
{ import: importSettings, export: exportSettings },
|
95
|
+
validationData
|
96
|
+
)}
|
97
|
+
onCancel={redirectToResult(history)}
|
98
|
+
error={error}
|
99
|
+
>
|
100
|
+
<SyncTypeRadios
|
101
|
+
name="syncType"
|
102
|
+
controlLabel="Action type"
|
103
|
+
radios={initRadioButtons(syncType)}
|
104
|
+
/>
|
105
|
+
<SyncSettingsFields
|
106
|
+
importSettings={importSettings}
|
107
|
+
exportSettings={exportSettings}
|
108
|
+
syncType={syncType}
|
109
|
+
resetField={resetToDefault}
|
110
|
+
/>
|
111
|
+
</ForemanForm>
|
112
|
+
);
|
113
|
+
};
|
157
114
|
|
158
115
|
NewTemplateSyncForm.propTypes = {
|
159
116
|
importSettings: PropTypes.array,
|
@@ -166,8 +123,6 @@ NewTemplateSyncForm.propTypes = {
|
|
166
123
|
exportUrl: PropTypes.string.isRequired,
|
167
124
|
importUrl: PropTypes.string.isRequired,
|
168
125
|
submitForm: PropTypes.func.isRequired,
|
169
|
-
currentLocation: PropTypes.object.isRequired,
|
170
|
-
currentOrganization: PropTypes.object.isRequired,
|
171
126
|
};
|
172
127
|
|
173
128
|
NewTemplateSyncForm.defaultProps = {
|
data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormHelpers.js
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
import * as Yup from 'yup';
|
2
|
+
|
3
|
+
export const redirectToResult = history => () =>
|
4
|
+
history.push({ pathname: '/template_syncs/result' });
|
5
|
+
|
6
|
+
const repoFormat = formatAry => value => {
|
7
|
+
if (value === undefined) {
|
8
|
+
return true;
|
9
|
+
}
|
10
|
+
|
11
|
+
const valid = formatAry
|
12
|
+
.map(item => value.startsWith(item))
|
13
|
+
.reduce((memo, item) => item || memo, false);
|
14
|
+
|
15
|
+
return value && valid;
|
16
|
+
};
|
17
|
+
|
18
|
+
export const syncFormSchema = (syncType, settingsObj, validationData) => {
|
19
|
+
const schema = (settingsObj[syncType].asMutable() || []).reduce(
|
20
|
+
(memo, setting) => {
|
21
|
+
if (setting.name === 'repo') {
|
22
|
+
return {
|
23
|
+
...memo,
|
24
|
+
repo: Yup.string()
|
25
|
+
.test(
|
26
|
+
'repo-format',
|
27
|
+
`Invalid repo format, must start with one of: ${validationData.repo.join(
|
28
|
+
', '
|
29
|
+
)}`,
|
30
|
+
repoFormat(validationData.repo)
|
31
|
+
)
|
32
|
+
.required("can't be blank"),
|
33
|
+
};
|
34
|
+
}
|
35
|
+
return memo;
|
36
|
+
},
|
37
|
+
{}
|
38
|
+
);
|
39
|
+
|
40
|
+
return Yup.object().shape({
|
41
|
+
[syncType]: Yup.object().shape(schema),
|
42
|
+
});
|
43
|
+
};
|
@@ -2,8 +2,6 @@ import { connect } from 'react-redux';
|
|
2
2
|
|
3
3
|
import * as FormActions from 'foremanReact/redux/actions/common/forms';
|
4
4
|
|
5
|
-
import { selectLayout } from 'foremanReact/components/Layout/LayoutSelectors';
|
6
|
-
|
7
5
|
import NewTemplateSyncForm from './NewTemplateSyncForm';
|
8
6
|
|
9
7
|
import {
|
@@ -24,8 +22,6 @@ const mapStateToProps = (state, ownProps) => {
|
|
24
22
|
initialValues: { ...initialFormValues },
|
25
23
|
importSettings,
|
26
24
|
exportSettings,
|
27
|
-
currentOrganization: selectLayout(state).currentOrganization,
|
28
|
-
currentLocation: selectLayout(state).currentLocation,
|
29
25
|
};
|
30
26
|
};
|
31
27
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
// move to some sort of pagination helper in core
|
2
2
|
export const templatesPage = (templates, pagination) => {
|
3
|
-
const offset = (pagination.page - 1) * pagination.
|
3
|
+
const offset = (pagination.page - 1) * pagination.per_page;
|
4
4
|
|
5
|
-
return templates.slice(offset, offset + pagination.
|
5
|
+
return templates.slice(offset, offset + pagination.per_page);
|
6
6
|
};
|
@@ -4,7 +4,7 @@ exports[`TemplateSyncResultReducer should return initial state 1`] = `
|
|
4
4
|
Object {
|
5
5
|
"pagination": Object {
|
6
6
|
"page": 1,
|
7
|
-
"
|
7
|
+
"per_page": 20,
|
8
8
|
},
|
9
9
|
"resultAction": "",
|
10
10
|
"templates": Array [],
|
@@ -27,7 +27,7 @@ Object {
|
|
27
27
|
"branch": "master",
|
28
28
|
"pagination": Object {
|
29
29
|
"page": 1,
|
30
|
-
"
|
30
|
+
"per_page": 20,
|
31
31
|
},
|
32
32
|
"repo": "https://github.com/theforeman/community-templates.git",
|
33
33
|
"resultAction": "import",
|
@@ -2,13 +2,19 @@ import React from 'react';
|
|
2
2
|
import { ListView } from 'patternfly-react';
|
3
3
|
import PropTypes from 'prop-types';
|
4
4
|
|
5
|
-
import Pagination from 'foremanReact/components/Pagination
|
5
|
+
import Pagination from 'foremanReact/components/Pagination';
|
6
6
|
|
7
7
|
import SyncedTemplate from './SyncedTemplate';
|
8
8
|
import { templatesPage } from '../TemplateSyncResultHelpers';
|
9
9
|
import ListViewHeader from './ListViewHeader';
|
10
10
|
|
11
|
-
const SyncResultList = ({
|
11
|
+
const SyncResultList = ({
|
12
|
+
pagination,
|
13
|
+
pagination: { page, per_page: perPage },
|
14
|
+
pageChange,
|
15
|
+
templates,
|
16
|
+
editPaths,
|
17
|
+
}) => (
|
12
18
|
<ListView>
|
13
19
|
<ListViewHeader />
|
14
20
|
{templatesPage(templates, pagination).map((template, idx) => (
|
@@ -19,11 +25,10 @@ const SyncResultList = ({ pagination, pageChange, templates, editPaths }) => (
|
|
19
25
|
/>
|
20
26
|
))}
|
21
27
|
<Pagination
|
22
|
-
viewType="list"
|
23
28
|
itemCount={templates.length}
|
24
|
-
pagination={pagination}
|
25
29
|
onChange={pageChange}
|
26
|
-
|
30
|
+
page={page}
|
31
|
+
perPage={perPage}
|
27
32
|
/>
|
28
33
|
</ListView>
|
29
34
|
);
|
@@ -31,7 +36,7 @@ const SyncResultList = ({ pagination, pageChange, templates, editPaths }) => (
|
|
31
36
|
SyncResultList.propTypes = {
|
32
37
|
pagination: PropTypes.shape({
|
33
38
|
page: PropTypes.number,
|
34
|
-
|
39
|
+
per_page: PropTypes.number,
|
35
40
|
}).isRequired,
|
36
41
|
pageChange: PropTypes.func.isRequired,
|
37
42
|
templates: PropTypes.array.isRequired,
|
@@ -88,17 +88,11 @@ exports[`SyncResultList should render 1`] = `
|
|
88
88
|
}
|
89
89
|
}
|
90
90
|
/>
|
91
|
-
<
|
92
|
-
dropdownButtonId="template-sync-result-dropdown"
|
91
|
+
<Pagination
|
93
92
|
itemCount={5}
|
94
93
|
onChange={[Function]}
|
95
|
-
|
96
|
-
|
97
|
-
"page": 1,
|
98
|
-
"perPage": 20,
|
99
|
-
}
|
100
|
-
}
|
101
|
-
viewType="list"
|
94
|
+
page={1}
|
95
|
+
perPage={20}
|
102
96
|
/>
|
103
97
|
</ListView>
|
104
98
|
`;
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_templates
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 9.
|
4
|
+
version: 9.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Sutcliffe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diffy
|
@@ -69,7 +69,6 @@ files:
|
|
69
69
|
- app/controllers/ui_template_syncs_controller.rb
|
70
70
|
- app/helpers/foreman_templates_helper.rb
|
71
71
|
- app/models/concerns/foreman_templates/template_extensions.rb
|
72
|
-
- app/models/setting/template_sync.rb
|
73
72
|
- app/services/foreman_templates/action.rb
|
74
73
|
- app/services/foreman_templates/cleaner.rb
|
75
74
|
- app/services/foreman_templates/export_result.rb
|
@@ -89,6 +88,7 @@ files:
|
|
89
88
|
- app/views/ui_template_syncs/template_import_results.json.rabl
|
90
89
|
- config/routes.rb
|
91
90
|
- db/migrate/20180627134929_change_lock_setting.rb
|
91
|
+
- db/migrate/20211122154929_templates_settings_category_to_dsl.rb
|
92
92
|
- lib/foreman_templates.rb
|
93
93
|
- lib/foreman_templates/engine.rb
|
94
94
|
- lib/foreman_templates/version.rb
|
@@ -96,9 +96,10 @@ files:
|
|
96
96
|
- package.json
|
97
97
|
- webpack/ForemanTemplates.js
|
98
98
|
- webpack/Routes.js
|
99
|
+
- webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js
|
99
100
|
- webpack/__mocks__/foremanReact/common/helpers.js
|
100
101
|
- webpack/__mocks__/foremanReact/components/Layout/LayoutSelectors.js
|
101
|
-
- webpack/__mocks__/foremanReact/components/Pagination/
|
102
|
+
- webpack/__mocks__/foremanReact/components/Pagination/index.js
|
102
103
|
- webpack/__mocks__/foremanReact/components/common/forms/CommonForm.js
|
103
104
|
- webpack/__mocks__/foremanReact/components/common/forms/ForemanForm.js
|
104
105
|
- webpack/__mocks__/foremanReact/components/common/forms/TextField.js
|
@@ -120,6 +121,7 @@ files:
|
|
120
121
|
- webpack/components/NewTemplateSync/__tests__/__snapshots__/NewTemplateSyncSelectors.test.js.snap
|
121
122
|
- webpack/components/NewTemplateSync/components/ButtonTooltip.js
|
122
123
|
- webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncForm.js
|
124
|
+
- webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormHelpers.js
|
123
125
|
- webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormSelectors.js
|
124
126
|
- webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/NewTemplateSyncFormSelectors.test.js
|
125
127
|
- webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/__snapshots__/NewTemplateSyncFormSelectors.test.js.snap
|
@@ -198,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
200
|
- !ruby/object:Gem::Version
|
199
201
|
version: '0'
|
200
202
|
requirements: []
|
201
|
-
rubygems_version: 3.
|
203
|
+
rubygems_version: 3.1.2
|
202
204
|
signing_key:
|
203
205
|
specification_version: 4
|
204
206
|
summary: Template-syncing engine for Foreman
|
@@ -1,115 +0,0 @@
|
|
1
|
-
class Setting
|
2
|
-
class TemplateSync < ::Setting
|
3
|
-
self.include_root_in_json = false
|
4
|
-
|
5
|
-
def self.common_stripped_names
|
6
|
-
%w(verbose repo branch dirname filter negate)
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.import_stripped_names
|
10
|
-
%w(prefix associate force lock)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.export_stripped_names
|
14
|
-
%w(metadata_export_mode commit_msg)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.import_setting_names(except = [])
|
18
|
-
map_prefix omit_settings(common_stripped_names + import_stripped_names, except)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.export_setting_names(except = [])
|
22
|
-
map_prefix omit_settings(common_stripped_names + export_stripped_names, except)
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.map_prefix(stripped_names)
|
26
|
-
stripped_names.map { |item| "template_sync_#{item}" }
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.omit_settings(setting_names, except_array)
|
30
|
-
setting_names.reject { |name| except_array.include? name }
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.associate_types
|
34
|
-
{
|
35
|
-
'always' => _('Always'),
|
36
|
-
'new' => _('New'),
|
37
|
-
'never' => _('Never')
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.lock_types
|
42
|
-
{
|
43
|
-
'lock' => _('Lock'),
|
44
|
-
'keep_lock_new' => _('Keep, lock new'),
|
45
|
-
'keep' => _('Keep, do not lock new'),
|
46
|
-
'unlock' => _('Unlock')
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.metadata_export_mode_types
|
51
|
-
{
|
52
|
-
'refresh' => _('Refresh'),
|
53
|
-
'keep' => _('Keep'),
|
54
|
-
'remove' => _('Remove')
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
|
-
def short_name
|
59
|
-
name.split('template_sync_').last
|
60
|
-
end
|
61
|
-
|
62
|
-
def selection
|
63
|
-
selection_method = name.split('template_sync_').last.concat('_types')
|
64
|
-
return transformed_selection(selection_method) if self.class.respond_to?(selection_method)
|
65
|
-
|
66
|
-
[]
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.load_defaults
|
70
|
-
return unless super
|
71
|
-
|
72
|
-
%w(template_sync_filter template_sync_branch template_sync_prefix).each { |s| Setting::BLANK_ATTRS << s }
|
73
|
-
Setting::NOT_STRIPPED << 'template_sync_prefix'
|
74
|
-
|
75
|
-
self.transaction do
|
76
|
-
[
|
77
|
-
self.set('template_sync_verbose', N_('Choose verbosity for Rake task importing templates'), false, N_('Verbosity')),
|
78
|
-
self.set('template_sync_associate', N_('Associate templates to OS, organization and location'), 'new', N_('Associate'), nil, { :collection => Proc.new { self.associate_types } }),
|
79
|
-
self.set('template_sync_prefix', N_('The string that will be added as prefix to imported templates'), "", N_('Prefix')),
|
80
|
-
self.set('template_sync_dirname', N_('The directory within the Git repo containing the templates'), '/', N_('Dirname')),
|
81
|
-
self.set('template_sync_filter', N_('Import/export names matching this regex (case-insensitive; snippets are not filtered)'), '', N_('Filter')),
|
82
|
-
self.set('template_sync_repo', N_('Target path to import/export. Different protocols can be used, for example /tmp/dir, git://example.com, https://example.com, ssh://example.com. When exporting to /tmp, note that production deployments may be configured to use private tmp.'), 'https://github.com/theforeman/community-templates.git', N_('Repo')),
|
83
|
-
self.set('template_sync_negate', N_('Negate the filter for import/export'), false, N_('Negate')),
|
84
|
-
self.set('template_sync_branch', N_('Default branch in Git repo'), '', N_('Branch')),
|
85
|
-
self.set('template_sync_metadata_export_mode', N_('Default metadata export mode, refresh re-renders metadata, keep will keep existing metadata, remove exports template without metadata'), 'refresh', N_('Metadata export mode'), nil, { :collection => Proc.new { self.metadata_export_mode_types } }),
|
86
|
-
self.set('template_sync_force', N_('Should importing overwrite locked templates?'), false, N_('Force import')),
|
87
|
-
self.set('template_sync_lock', N_('How to handle lock for imported templates?'), 'keep', N_('Lock templates'), nil, { :collection => Proc.new { self.lock_types } }),
|
88
|
-
self.set('template_sync_commit_msg', N_('Custom commit message for templates export'), 'Templates export made by a Foreman user', N_('Commit message'))
|
89
|
-
].compact.each { |s| self.create! s.update(:category => "Setting::TemplateSync") }
|
90
|
-
end
|
91
|
-
|
92
|
-
true
|
93
|
-
end
|
94
|
-
|
95
|
-
def validate_template_sync_associate(record)
|
96
|
-
values = record.class.associate_types.keys
|
97
|
-
if record.value && !values.include?(record.value)
|
98
|
-
record.errors[:base] << (_("template_sync_associate must be one of %s") % values.join(', '))
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def validate_template_sync_metadata_export_mode(record)
|
103
|
-
values = record.class.metadata_export_mode_types.keys
|
104
|
-
if record.value && !values.include?(record.value)
|
105
|
-
record.errors[:base] << (_("template_sync_metadata_export_mode must be one of %s") % values.join(', '))
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def transformed_selection(selection_method)
|
112
|
-
self.class.public_send(selection_method).map { |key, translated| { :value => key, :label => translated } }
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|