foreman_templates 9.0.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/template_controller.rb +3 -3
  3. data/app/controllers/ui_template_syncs_controller.rb +9 -3
  4. data/app/services/foreman_templates/template_exporter.rb +1 -1
  5. data/app/services/foreman_templates/template_importer.rb +1 -1
  6. data/app/views/template_sync_settings/show.json.rabl +2 -2
  7. data/app/views/template_syncs/index.html.erb +14 -17
  8. data/app/views/ui_template_syncs/template_attrs.json.rabl +1 -1
  9. data/db/migrate/20211122154929_templates_settings_category_to_dsl.rb +5 -0
  10. data/lib/foreman_templates/engine.rb +76 -10
  11. data/lib/foreman_templates/version.rb +1 -1
  12. data/lib/foreman_templates.rb +15 -0
  13. data/lib/tasks/foreman_templates_tasks.rake +10 -9
  14. data/webpack/ForemanTemplates.js +17 -13
  15. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +2 -0
  16. data/webpack/__mocks__/foremanReact/components/Pagination/index.js +2 -0
  17. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncForm.js +95 -140
  18. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormHelpers.js +43 -0
  19. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/index.js +0 -4
  20. data/webpack/components/TemplateSyncResult/TemplateSyncResultHelpers.js +2 -2
  21. data/webpack/components/TemplateSyncResult/TemplateSyncResultReducer.js +1 -1
  22. data/webpack/components/TemplateSyncResult/__tests__/__snapshots__/TemplateSyncResultReducer.test.js.snap +2 -2
  23. data/webpack/components/TemplateSyncResult/components/SyncResultList.js +11 -6
  24. data/webpack/components/TemplateSyncResult/components/__tests__/SyncResultList.test.js +1 -1
  25. data/webpack/components/TemplateSyncResult/components/__tests__/__snapshots__/SyncResultList.test.js.snap +3 -9
  26. metadata +7 -5
  27. data/app/models/setting/template_sync.rb +0 -115
  28. 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: 20a8db347037219c9d8b4c09906b703e7c9370a1c5fd9775ce7d2261f82f690f
4
- data.tar.gz: 5bea2c74fb7d677dca8d203607ef51ade2de42e26b8e458c739e80240c3cd7bf
3
+ metadata.gz: bda8e472a4ae76b0f1480738e9059423a249c4156842deebc385baa2f3c2a5ca
4
+ data.tar.gz: bb04b38ce6791e62f90fcea5a177d38ca62402e6c3037e7f3ed85c416f91696c
5
5
  SHA512:
6
- metadata.gz: 40eab594f513b94c2deec58e35c4f03030379598db6a482bc201838b227bcfafd6f85e5c16eefab830da3d4950109b09d67a9a9032971a7059a1fcb87f3b0fee
7
- data.tar.gz: 4ca1502b153c698da0461087a1e8976ac1429def1d5532012639abf5724a6c010682161b0c1d8606f6d21eebd9df997dc0e1288abc4f3638f60edbd37124666b
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, Setting::TemplateSync.associate_types.keys, :required => false, :desc => N_("Associate to OS's, Locations & Organizations. Options are: always, new or never.")
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, Setting::TemplateSync.lock_types.keys + ["true", "false", "0", "1"], :required => false, :desc => N_("Lock imported templates")
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, Setting::TemplateSync.metadata_export_mode_types.keys, :required => false, :desc => N_("Specify how to handle metadata")
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 = Setting.where :name => Setting::TemplateSync.import_setting_names(['verbose'])
12
- export_settings = Setting.where :name => Setting::TemplateSync.export_setting_names(['verbose'])
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 = template.name =~ /#{filter}/i ? !negate : negate
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.short_name }
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.selection }
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
- <div id="foreman-templates"/>
9
-
10
- <%= mount_react_component('ForemanTemplates',
11
- '#foreman-templates',
12
- { :apiUrls => {
13
- :exportUrl => export_ui_template_syncs_path,
14
- :syncSettingsUrl => sync_settings_ui_template_syncs_path,
15
- :importUrl => import_ui_template_syncs_path
16
- },
17
- :validationData => { :repo => ForemanTemplates::Action.repo_start_with },
18
- :editPaths => edit_paths,
19
- :fileRepoStartWith => ForemanTemplates::Action.file_repo_start_with,
20
- :userPermissions => {
21
- :import => authorized_for(:controller => :ui_template_syncs, :action => :import),
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 { |part| part.capitalize }.join(' ')
14
+ template.class.name.underscore.split('_').map(&:capitalize).join(' ')
15
15
  end
16
16
 
17
17
  node(:can_edit) do |template|
@@ -0,0 +1,5 @@
1
+ class TemplatesSettingsCategoryToDsl < ActiveRecord::Migration[6.0]
2
+ def up
3
+ Setting.where(category: 'Setting::TemplateSync').update_all(category: 'Setting')
4
+ end
5
+ end
@@ -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 '>= 1.24'
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
- :url_hash => { :controller => :template_syncs, :action => :index },
53
- :caption => N_('Sync Templates'),
54
- :parent => :hosts_menu,
55
- :before => :ptables,
56
- :turbolinks => false
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
@@ -1,3 +1,3 @@
1
1
  module ForemanTemplates
2
- VERSION = '9.0.0'.freeze
2
+ VERSION = '9.2.0'.freeze
3
3
  end
@@ -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: verbose,
24
- repo: ENV['repo'],
25
- branch: ENV['branch'],
26
- prefix: ENV['prefix'],
27
- dirname: ENV['dirname'],
28
- filter: ENV['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: ENV['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: ENV['negate'],
62
- prefix: ENV['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"]
@@ -4,26 +4,30 @@ import PropTypes from 'prop-types';
4
4
 
5
5
  import Routes from './Routes';
6
6
 
7
- const ForemanTemplates = ({ data }) => (
7
+ const ForemanTemplates = ({
8
+ apiUrls,
9
+ validationData,
10
+ fileRepoStartWith,
11
+ userPermissions,
12
+ editPaths,
13
+ }) => (
8
14
  <Router>
9
15
  <Routes
10
- apiUrls={data.apiUrls}
11
- validationData={data.validationData}
12
- editPaths={data.editPaths}
13
- fileRepoStartWith={data.fileRepoStartWith}
14
- userPermissions={data.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
- data: PropTypes.shape({
21
- apiUrls: PropTypes.object,
22
- validationData: PropTypes.object,
23
- editPaths: PropTypes.object,
24
- userPermissions: PropTypes.object,
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;
@@ -0,0 +1,2 @@
1
+ export const useForemanOrganization = () => {};
2
+ export const useForemanLocation = () => {};
@@ -0,0 +1,2 @@
1
+ const Pagination = () => jest.fn();
2
+ export default Pagination;
@@ -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 * as Yup from 'yup';
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
- const redirectToResult = history => () =>
11
- history.push({ pathname: '/template_syncs/result' });
12
-
13
- const repoFormat = formatAry => value => {
14
- if (value === undefined) {
15
- return true;
16
- }
17
-
18
- const valid = formatAry
19
- .map(item => value.startsWith(item))
20
- .reduce((memo, item) => item || memo, false);
21
-
22
- return value && valid;
23
- };
24
-
25
- const syncFormSchema = (syncType, settingsObj, validationData) => {
26
- const schema = (settingsObj[syncType].asMutable() || []).reduce(
27
- (memo, setting) => {
28
- if (setting.name === 'repo') {
29
- return {
30
- ...memo,
31
- repo: Yup.string()
32
- .test(
33
- 'repo-format',
34
- `Invalid repo format, must start with one of: ${validationData.repo.join(
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
- return Yup.object().shape({
48
- [syncType]: Yup.object().shape(schema),
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
- this.allowedSyncType(this.props.userPermissions, buttonAttrs)
47
+ allowedSyncType(userPermissions, buttonAttrs)
78
48
  );
79
49
 
80
- initRadioButtons = syncType =>
81
- this.permitRadioButtons(this.radioButtons).map(buttonAttrs => ({
50
+ const initRadioButtons = templateSyncType =>
51
+ permitRadioButtons(radioButtons).map(buttonAttrs => ({
82
52
  get checked() {
83
- return this.value === syncType;
53
+ return buttonAttrs.value === templateSyncType;
84
54
  },
85
- onChange: this.updateSyncType,
55
+ onChange: updateSyncType,
86
56
  ...buttonAttrs,
87
57
  }));
88
58
 
89
- render() {
90
- const {
91
- error,
92
- submitForm,
93
- importSettings,
94
- exportSettings,
95
- history,
96
- validationData,
97
- importUrl,
98
- exportUrl,
99
- initialValues,
100
- currentLocation,
101
- currentOrganization,
102
- } = this.props;
103
-
104
- const addTaxParams = (key, currentTax) => params => {
105
- if (currentTax.id) {
106
- params[key] = [currentTax.id];
107
- }
108
- return params;
109
- };
110
-
111
- const addOrgParams = addTaxParams('organization_ids', currentOrganization);
112
- const addLocParams = addTaxParams('location_ids', currentLocation);
113
-
114
- const resetToDefault = (fieldName, fieldValue) => resetFn =>
115
- resetFn(fieldName, fieldValue);
116
-
117
- return (
118
- <ForemanForm
119
- onSubmit={(values, actions) => {
120
- const url = this.state.syncType === 'import' ? importUrl : exportUrl;
121
- return submitForm({
122
- url,
123
- values: compose(
124
- addLocParams,
125
- addOrgParams
126
- )(values[this.state.syncType]),
127
- message: `Templates were ${this.state.syncType}ed.`,
128
- item: 'TemplateSync',
129
- }).then(args => {
130
- history.replace({ pathname: '/template_syncs/result' });
131
- });
132
- }}
133
- initialValues={initialValues}
134
- validationSchema={syncFormSchema(
135
- this.state.syncType,
136
- { import: importSettings, export: exportSettings },
137
- validationData
138
- )}
139
- onCancel={redirectToResult(history)}
140
- error={error}
141
- >
142
- <SyncTypeRadios
143
- name="syncType"
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 = {
@@ -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.perPage;
3
+ const offset = (pagination.page - 1) * pagination.per_page;
4
4
 
5
- return templates.slice(offset, offset + pagination.perPage);
5
+ return templates.slice(offset, offset + pagination.per_page);
6
6
  };
@@ -12,7 +12,7 @@ export const initialState = Immutable({
12
12
 
13
13
  pagination: {
14
14
  page: 1,
15
- perPage: 20,
15
+ per_page: 20,
16
16
  },
17
17
  });
18
18
 
@@ -4,7 +4,7 @@ exports[`TemplateSyncResultReducer should return initial state 1`] = `
4
4
  Object {
5
5
  "pagination": Object {
6
6
  "page": 1,
7
- "perPage": 20,
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
- "perPage": 20,
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/PaginationWrapper';
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 = ({ pagination, pageChange, templates, editPaths }) => (
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
- dropdownButtonId="template-sync-result-dropdown"
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
- perPage: PropTypes.number,
39
+ per_page: PropTypes.number,
35
40
  }).isRequired,
36
41
  pageChange: PropTypes.func.isRequired,
37
42
  templates: PropTypes.array.isRequired,
@@ -18,7 +18,7 @@ const fixtures = {
18
18
  editPaths,
19
19
  pagination: {
20
20
  page: 1,
21
- perPage: 20,
21
+ per_page: 20,
22
22
  },
23
23
  },
24
24
  };
@@ -88,17 +88,11 @@ exports[`SyncResultList should render 1`] = `
88
88
  }
89
89
  }
90
90
  />
91
- <PaginationWrapper
92
- dropdownButtonId="template-sync-result-dropdown"
91
+ <Pagination
93
92
  itemCount={5}
94
93
  onChange={[Function]}
95
- pagination={
96
- Object {
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.0.0
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: 2020-05-18 00:00:00.000000000 Z
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/PaginationWrapper.js
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.0.8
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
@@ -1,2 +0,0 @@
1
- const PaginationWrapper = () => jest.fn();
2
- export default PaginationWrapper;