foreman_resource_quota 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +51 -0
  4. data/Rakefile +49 -0
  5. data/app/controllers/concerns/foreman/controller/parameters/resource_quota.rb +28 -0
  6. data/app/controllers/foreman_resource_quota/api/v2/resource_quotas_controller.rb +96 -0
  7. data/app/controllers/foreman_resource_quota/application_controller.rb +9 -0
  8. data/app/controllers/foreman_resource_quota/resource_quotas_controller.rb +50 -0
  9. data/app/helpers/foreman_resource_quota/hosts_helper.rb +18 -0
  10. data/app/helpers/foreman_resource_quota/resource_quota_helper.rb +107 -0
  11. data/app/models/concerns/foreman_resource_quota/host_managed_extensions.rb +115 -0
  12. data/app/models/concerns/foreman_resource_quota/user_extensions.rb +15 -0
  13. data/app/models/concerns/foreman_resource_quota/usergroup_extensions.rb +14 -0
  14. data/app/models/foreman_resource_quota/resource_quota.rb +83 -0
  15. data/app/models/foreman_resource_quota/resource_quota_user.rb +10 -0
  16. data/app/models/foreman_resource_quota/resource_quota_usergroup.rb +10 -0
  17. data/app/services/foreman_resource_quota/resource_origin.rb +97 -0
  18. data/app/services/foreman_resource_quota/resource_origins/compute_attributes_origin.rb +64 -0
  19. data/app/services/foreman_resource_quota/resource_origins/compute_resource_origin.rb +82 -0
  20. data/app/services/foreman_resource_quota/resource_origins/facts_origin.rb +68 -0
  21. data/app/services/foreman_resource_quota/resource_origins/vm_attributes_origin.rb +40 -0
  22. data/app/views/foreman_resource_quota/api/v2/hosts/resource_quota.json.rabl +3 -0
  23. data/app/views/foreman_resource_quota/api/v2/resource_quotas/base.json.rabl +6 -0
  24. data/app/views/foreman_resource_quota/api/v2/resource_quotas/create.json.rabl +5 -0
  25. data/app/views/foreman_resource_quota/api/v2/resource_quotas/hosts.json.rabl +7 -0
  26. data/app/views/foreman_resource_quota/api/v2/resource_quotas/index.json.rabl +5 -0
  27. data/app/views/foreman_resource_quota/api/v2/resource_quotas/main.json.rabl +7 -0
  28. data/app/views/foreman_resource_quota/api/v2/resource_quotas/show.json.rabl +5 -0
  29. data/app/views/foreman_resource_quota/api/v2/resource_quotas/update.json.rabl +5 -0
  30. data/app/views/foreman_resource_quota/api/v2/resource_quotas/usergroups.json.rabl +7 -0
  31. data/app/views/foreman_resource_quota/api/v2/resource_quotas/users.json.rabl +7 -0
  32. data/app/views/foreman_resource_quota/api/v2/resource_quotas/utilization.json.rabl +7 -0
  33. data/app/views/foreman_resource_quota/api/v2/usergroups/resource_quota.json.rabl +3 -0
  34. data/app/views/foreman_resource_quota/api/v2/users/resource_quota.json.rabl +3 -0
  35. data/app/views/foreman_resource_quota/resource_quotas/_form.html.erb +21 -0
  36. data/app/views/foreman_resource_quota/resource_quotas/edit.html.erb +12 -0
  37. data/app/views/foreman_resource_quota/resource_quotas/index.html.erb +55 -0
  38. data/app/views/foreman_resource_quota/resource_quotas/new.html.erb +10 -0
  39. data/app/views/foreman_resource_quota/resource_quotas/welcome.html.erb +10 -0
  40. data/app/views/hosts/_form_quota_fields.html.erb +4 -0
  41. data/app/views/users/_form_quota_tab.html.erb +45 -0
  42. data/config/initializers/inflections.rb +5 -0
  43. data/config/routes.rb +43 -0
  44. data/db/migrate/20230306120001_create_resource_quotas.rb +31 -0
  45. data/lib/foreman_resource_quota/engine.rb +56 -0
  46. data/lib/foreman_resource_quota/exceptions.rb +11 -0
  47. data/lib/foreman_resource_quota/register.rb +106 -0
  48. data/lib/foreman_resource_quota/version.rb +5 -0
  49. data/lib/foreman_resource_quota.rb +6 -0
  50. data/lib/tasks/foreman_resource_quota_tasks.rake +50 -0
  51. data/locale/Makefile +60 -0
  52. data/locale/en/foreman_resource_quota.po +18 -0
  53. data/locale/foreman_resource_quota.pot +19 -0
  54. data/locale/gemspec.rb +4 -0
  55. data/package.json +44 -0
  56. data/webpack/api_helper.js +113 -0
  57. data/webpack/api_helper.test.js +96 -0
  58. data/webpack/components/CreateResourceQuotaModal.js +46 -0
  59. data/webpack/components/ResourceQuotaEmptyState/index.js +58 -0
  60. data/webpack/components/ResourceQuotaForm/ResourceQuotaForm.scss +1 -0
  61. data/webpack/components/ResourceQuotaForm/ResourceQuotaFormConstants.js +71 -0
  62. data/webpack/components/ResourceQuotaForm/components/Properties/Properties.scss +9 -0
  63. data/webpack/components/ResourceQuotaForm/components/Properties/StaticDetail.js +72 -0
  64. data/webpack/components/ResourceQuotaForm/components/Properties/StatusPropertiesLabel.js +71 -0
  65. data/webpack/components/ResourceQuotaForm/components/Properties/StatusPropertiesLabel.test.js +50 -0
  66. data/webpack/components/ResourceQuotaForm/components/Properties/TextInputField.js +131 -0
  67. data/webpack/components/ResourceQuotaForm/components/Properties/index.js +190 -0
  68. data/webpack/components/ResourceQuotaForm/components/QuotaState.js +157 -0
  69. data/webpack/components/ResourceQuotaForm/components/Resource/Resource.scss +13 -0
  70. data/webpack/components/ResourceQuotaForm/components/Resource/UnitInputField.js +224 -0
  71. data/webpack/components/ResourceQuotaForm/components/Resource/UtilizationProgress.js +151 -0
  72. data/webpack/components/ResourceQuotaForm/components/Resource/UtilizationProgress.scss +10 -0
  73. data/webpack/components/ResourceQuotaForm/components/Resource/index.js +239 -0
  74. data/webpack/components/ResourceQuotaForm/components/Resources.js +105 -0
  75. data/webpack/components/ResourceQuotaForm/components/Submit.js +72 -0
  76. data/webpack/components/ResourceQuotaForm/index.js +185 -0
  77. data/webpack/components/UpdateResourceQuotaModal.js +143 -0
  78. data/webpack/global_index.js +15 -0
  79. data/webpack/global_test_setup.js +11 -0
  80. data/webpack/helper.js +86 -0
  81. data/webpack/index.js +23 -0
  82. data/webpack/lib/ActionableDetail.js +115 -0
  83. data/webpack/lib/ActionableDetail.scss +4 -0
  84. data/webpack/lib/EditableSwitch.js +47 -0
  85. data/webpack/lib/EditableTextInput/EditableTextInput.js +206 -0
  86. data/webpack/lib/EditableTextInput/PencilEditButton.js +27 -0
  87. data/webpack/lib/EditableTextInput/__tests__/editableTextInput.test.js +193 -0
  88. data/webpack/lib/EditableTextInput/editableTextInput.scss +38 -0
  89. data/webpack/lib/EditableTextInput/index.js +4 -0
  90. data/webpack/lib/react-testing-lib-wrapper.js +80 -0
  91. data/webpack/test_setup.js +17 -0
  92. metadata +134 -0
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateResourceQuotas < ActiveRecord::Migration[6.1]
4
+ # rubocop: disable Metrics/AbcSize
5
+ def change
6
+ create_table :resource_quotas do |t|
7
+ t.string :name, null: false
8
+ t.text :description
9
+ t.integer :cpu_cores, default: nil
10
+ t.integer :memory_mb, default: nil
11
+ t.integer :disk_gb, default: nil
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ create_table :resource_quotas_usergroups do |t|
17
+ t.belongs_to :resource_quota
18
+ t.belongs_to :usergroup
19
+ t.timestamps
20
+ end
21
+
22
+ create_table :resource_quotas_users do |t|
23
+ t.belongs_to :resource_quota
24
+ t.belongs_to :user
25
+ t.timestamps
26
+ end
27
+ add_reference :hosts, :resource_quota, foreign_key: { to_table: :resource_quotas }
28
+ add_column :users, :resource_quota_is_optional, :boolean, default: false
29
+ end
30
+ # rubocop: enable Metrics/AbcSize
31
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanResourceQuota
4
+ class Engine < ::Rails::Engine
5
+ engine_name 'foreman_resource_quota'
6
+
7
+ config.autoload_paths += Dir["#{config.root}/app/models/"]
8
+ config.autoload_paths += Dir["#{config.root}/app/controllers/"]
9
+ config.autoload_paths += Dir["#{config.root}/app/views/"]
10
+ config.autoload_paths += Dir["#{config.root}/app/services/foreman_resource_quota/"]
11
+ config.autoload_paths += Dir["#{config.root}/app/helpers/foreman_resource_quota/"]
12
+ config.autoload_paths += Dir["#{config.root}/lib/"]
13
+
14
+ # Add db migrations
15
+ initializer 'foreman_resource_quota.load_app_instance_data' do |app|
16
+ ForemanResourceQuota::Engine.paths['db/migrate'].existent.each do |path|
17
+ app.config.paths['db/migrate'] << path
18
+ end
19
+ end
20
+
21
+ # Apipie
22
+ initializer 'foreman_resource_quota.apipie' do
23
+ Apipie.configuration.checksum_path += ['/foreman_resource_quota/api/']
24
+ Rabl.configure do |config|
25
+ config.view_paths << ForemanResourceQuota::Engine.root.join('app', 'views', 'foreman_resource_quota')
26
+ end
27
+ end
28
+
29
+ # Rake tasks
30
+ rake_tasks do
31
+ Rake::Task['db:seed'].enhance do
32
+ ForemanResourceQuota::Engine.load_seed
33
+ end
34
+ end
35
+
36
+ # Plugin extensions
37
+ initializer 'foreman_resource_quota.register_plugin', before: :finisher_hook do |_app|
38
+ require 'foreman_resource_quota/register'
39
+ end
40
+
41
+ # Include concerns in this config.to_prepare block
42
+ config.to_prepare do
43
+ ::User.include ForemanResourceQuota::UserExtensions
44
+ ::Usergroup.include ForemanResourceQuota::UsergroupExtensions
45
+ ::Host::Managed.include ForemanResourceQuota::HostManagedExtensions
46
+ rescue StandardError => e
47
+ Rails.logger.warn "ForemanResourceQuota: skipping engine hook (#{e})"
48
+ end
49
+
50
+ initializer 'foreman_resource_quota.register_gettext', after: :load_config_initializers do |_app|
51
+ locale_dir = File.join(File.expand_path('../..', __dir__), 'locale')
52
+ locale_domain = 'foreman_resource_quota'
53
+ Foreman::Gettext::Support.add_text_domain locale_domain, locale_dir
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanResourceQuota
4
+ module Exceptions
5
+ class ResourceQuotaException < Foreman::Exception; end
6
+ class HostResourceQuotaEmptyException < ResourceQuotaException; end
7
+ class ResourceLimitException < ResourceQuotaException; end
8
+ class HostResourcesException < ResourceQuotaException; end
9
+ class ResourceQuotaUtilizationException < ResourceQuotaException; end
10
+ end
11
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop: disable Metrics/BlockLength
4
+ Foreman::Plugin.register :foreman_resource_quota do
5
+ requires_foreman '>= 3.5.0'
6
+ # Apipie
7
+ apipie_documented_controllers ["#{ForemanResourceQuota::Engine.root}" \
8
+ '/app/controllers/foreman_resource_quot/api/v2/*.rb']
9
+
10
+ # Add permissions
11
+ security_block :foreman_resource_quota do
12
+ permission 'view_foreman_resource_quota/resource_quotas',
13
+ { 'foreman_resource_quota/resource_quotas': %i[index welcome auto_complete_search],
14
+ 'foreman_resource_quota/api/v2/resource_quotas': %i[index show utilization hosts users usergroups
15
+ auto_complete_search],
16
+ 'foreman_resource_quota/api/v2/resource_quotas/:resource_quota_id/': %i[utilization hosts users usergroups] },
17
+ resource_type: 'ForemanResourceQuota::ResourceQuota'
18
+ permission 'create_foreman_resource_quota/resource_quotas',
19
+ { 'foreman_resource_quota/resource_quotas': %i[new create],
20
+ 'foreman_resource_quota/api/v2/resource_quotas': %i[create] },
21
+ resource_type: 'ForemanResourceQuota::ResourceQuota'
22
+ permission 'edit_foreman_resource_quota/resource_quotas',
23
+ { 'foreman_resource_quota/resource_quotas': %i[edit update],
24
+ 'foreman_resource_quota/api/v2/resource_quotas': %i[update] },
25
+ resource_type: 'ForemanResourceQuota::ResourceQuota'
26
+ permission 'destroy_foreman_resource_quota/resource_quotas',
27
+ { 'foreman_resource_quota/resource_quotas': %i[destroy],
28
+ 'foreman_resource_quota/api/v2/resource_quotas': %i[destroy] },
29
+ resource_type: 'ForemanResourceQuota::ResourceQuota'
30
+
31
+ # TODO: Evaluate whether host/user/usergroup permission extensions are necessary
32
+ end
33
+
34
+ # Add a permissions to default roles (Viewer and Manager)
35
+ role 'Resource Quota Manager', ['view_foreman_resource_quota/resource_quotas',
36
+ 'create_foreman_resource_quota/resource_quotas',
37
+ 'edit_foreman_resource_quota/resource_quotas',
38
+ 'destroy_foreman_resource_quota/resource_quotas',
39
+ 'view_hosts',
40
+ 'edit_hosts',
41
+ 'view_users',
42
+ 'edit_users']
43
+ role 'Resource Quota User', ['view_foreman_resource_quota/resource_quotas',
44
+ 'view_hosts',
45
+ 'view_users',
46
+ 'view_usergroups']
47
+ add_all_permissions_to_default_roles
48
+
49
+ # add controller parameter extension
50
+ parameter_filter User, { resource_quotas: [], resource_quota_ids: [] }, :resource_quota_is_optional
51
+ parameter_filter Usergroup, { resource_quotas: [], resource_quota_ids: [] }
52
+ parameter_filter Host::Managed, :resource_quota_id
53
+
54
+ # add UI menu extension
55
+ add_menu_item :top_menu, :resource_quotas,
56
+ url_hash: { controller: 'foreman_resource_quota/resource_quotas', action: :index },
57
+ caption: N_('Resource Quotas'),
58
+ parent: :configure_menu,
59
+ after: :common_parameters
60
+
61
+ # add API extension
62
+ extend_rabl_template 'api/v2/hosts/main', 'foreman_resource_quota/api/v2/hosts/resource_quota'
63
+ extend_rabl_template 'api/v2/users/main', 'foreman_resource_quota/api/v2/users/resource_quota'
64
+ extend_rabl_template 'api/v2/usergroups/main', 'foreman_resource_quota/api/v2/usergroups/resource_quota'
65
+
66
+ # add UI user/usergroup/hosts extension
67
+ extend_page 'users/_form' do |cx|
68
+ cx.add_pagelet :main_tabs,
69
+ id: :quota_user_tab,
70
+ name: N_('Resource Quota'),
71
+ resource_type: :user,
72
+ partial: 'users/form_quota_tab'
73
+ end
74
+ extend_page 'usergroups/_form' do |cx|
75
+ cx.add_pagelet :main_tabs,
76
+ id: :quota_usergroup_tab,
77
+ name: N_('Resource Quota'),
78
+ resource_type: :usergroup,
79
+ partial: 'users/form_quota_tab'
80
+ end
81
+ extend_page 'hosts/_form' do |cx|
82
+ cx.add_pagelet :main_tab_fields,
83
+ id: :quota_hosts_tab_fields,
84
+ resource_type: :host,
85
+ partial: 'hosts/form_quota_fields'
86
+ end
87
+
88
+ # Add global Foreman settings
89
+ settings do
90
+ category :provisioning do
91
+ setting 'resource_quota_optional_assignment',
92
+ type: :boolean,
93
+ default: false,
94
+ full_name: N_('Resource Quota optional assignment'),
95
+ description: N_('Make the assignment of a Resource quota, during the host creation process, optional for
96
+ everyone. If this is true, user-specific "optional assignment" configurations are neglected.')
97
+ setting 'resource_quota_global_no_action',
98
+ type: :boolean,
99
+ default: true,
100
+ full_name: N_('Global Resource Quota no action'),
101
+ description: N_('Take no action when a resource quota is exceeded.')
102
+ # Future: Overwrite quota-specific "out of resource"-action and take no ..
103
+ end
104
+ end
105
+ end
106
+ # rubocop: enable Metrics/BlockLength
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanResourceQuota
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'foreman_resource_quota/engine'
4
+
5
+ module ForemanResourceQuota
6
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/testtask'
4
+
5
+ # Tasks
6
+ namespace :foreman_resource_quota do
7
+ namespace :example do
8
+ desc 'Example Task'
9
+ task task: :environment do
10
+ # Task goes here
11
+ end
12
+ end
13
+ end
14
+
15
+ # Tests
16
+ namespace :test do
17
+ desc 'Test ForemanResourceQuota'
18
+ Rake::TestTask.new(:foreman_resource_quota) do |t|
19
+ test_dir = File.expand_path('../../test', __dir__)
20
+ t.libs << 'test'
21
+ t.libs << test_dir
22
+ t.pattern = "#{test_dir}/**/*_test.rb"
23
+ t.verbose = true
24
+ t.warning = false
25
+ end
26
+ end
27
+
28
+ namespace :foreman_resource_quota do
29
+ task rubocop: :environment do
30
+ begin
31
+ require 'rubocop/rake_task'
32
+ RuboCop::RakeTask.new(:rubocop_foreman_resource_quota) do |task|
33
+ task.patterns = ["#{ForemanResourceQuota::Engine.root}/app/**/*.rb",
34
+ "#{ForemanResourceQuota::Engine.root}/lib/**/*.rb",
35
+ "#{ForemanResourceQuota::Engine.root}/test/**/*.rb"]
36
+ end
37
+ rescue StandardError
38
+ puts 'Rubocop not loaded.'
39
+ end
40
+
41
+ Rake::Task['rubocop_foreman_resource_quota'].invoke
42
+ end
43
+ end
44
+
45
+ Rake::Task[:test].enhance ['test:foreman_resource_quota']
46
+
47
+ load 'tasks/jenkins.rake'
48
+ if Rake::Task.task_defined?(:'jenkins:unit')
49
+ Rake::Task['jenkins:unit'].enhance ['test:foreman_resource_quota', 'foreman_resource_quota:rubocop']
50
+ end
data/locale/Makefile ADDED
@@ -0,0 +1,60 @@
1
+ #
2
+ # Makefile for PO merging and MO generation. More info in the README.
3
+ #
4
+ # make all-mo (default) - generate MO files
5
+ # make check - check translations using translate-tool
6
+ # make tx-update - download and merge translations from Transifex
7
+ # make clean - clean everything
8
+ #
9
+ DOMAIN = foreman_resource_quota
10
+ VERSION = $(shell ruby -e 'require "rubygems";spec = Gem::Specification::load(Dir.glob("../*.gemspec")[0]);puts spec.version')
11
+ POTFILE = $(DOMAIN).pot
12
+ MOFILE = $(DOMAIN).mo
13
+ POFILES = $(shell find . -name '$(DOMAIN).po')
14
+ MOFILES = $(patsubst %.po,%.mo,$(POFILES))
15
+ POXFILES = $(patsubst %.po,%.pox,$(POFILES))
16
+ EDITFILES = $(patsubst %.po,%.edit.po,$(POFILES))
17
+
18
+ %.mo: %.po
19
+ mkdir -p $(shell dirname $@)/LC_MESSAGES
20
+ msgfmt -o $(shell dirname $@)/LC_MESSAGES/$(MOFILE) $<
21
+
22
+ # Generate MO files from PO files
23
+ all-mo: $(MOFILES)
24
+
25
+ # Check for malformed strings
26
+ %.pox: %.po
27
+ msgfmt -c $<
28
+ pofilter --nofuzzy -t variables -t blank -t urls -t emails -t long -t newlines \
29
+ -t endwhitespace -t endpunc -t puncspacing -t options -t printf -t validchars --gnome $< > $@
30
+ cat $@
31
+ ! grep -q msgid $@
32
+
33
+ %.edit.po:
34
+ touch $@
35
+
36
+ check: $(POXFILES)
37
+
38
+ # Unify duplicate translations
39
+ uniq-po:
40
+ for f in $(shell find ./ -name "*.po") ; do \
41
+ msguniq $$f -o $$f ; \
42
+ done
43
+
44
+ tx-pull: $(EDITFILES)
45
+ tx pull -f
46
+ for f in $(EDITFILES) ; do \
47
+ sed -i 's/^\("Project-Id-Version: \).*$$/\1$(DOMAIN) $(VERSION)\\n"/' $$f; \
48
+ done
49
+
50
+ tx-update: tx-pull
51
+ @echo
52
+ @echo Run rake plugin:gettext[$(DOMAIN)] from the Foreman installation, then make -C locale mo-files to finish
53
+ @echo
54
+
55
+ mo-files: $(MOFILES)
56
+ git add $(POFILES) $(POTFILE) ../locale/*/LC_MESSAGES
57
+ git commit -m "i18n - pulling from tx"
58
+ @echo
59
+ @echo Changes commited!
60
+ @echo
@@ -0,0 +1,18 @@
1
+ # foreman_resource_quota
2
+ #
3
+ # This file is distributed under the same license as foreman_resource_quota.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:54+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -0,0 +1,19 @@
1
+ # foreman_resource_quota
2
+ #
3
+ # This file is distributed under the same license as foreman_resource_quota.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:46+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
19
+
data/locale/gemspec.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Matches foreman_resource_quota.gemspec
4
+ _('Foreman Plug-in to manage resource usage among users.')
data/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "foreman_resource_quota",
3
+ "version": "1.0.0",
4
+ "description": "DESCRIPTION",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "lint": "tfm-lint --plugin -d /webpack",
8
+ "test": "tfm-test --plugin --config jest.config.js",
9
+ "test:watch": "tfm-test --plugin --watchAll",
10
+ "test:current": "tfm-test --plugin --watch",
11
+ "publish-coverage": "tfm-publish-coverage",
12
+ "stories": "tfm-stories --plugin",
13
+ "stories:build": "tfm-build-stories --plugin",
14
+ "create-react-component": "yo react-domain"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/theforeman/foreman_resource_quota.git"
19
+ },
20
+ "bugs": {
21
+ "url": "http://projects.theforeman.org/projects/foreman_resource_quota/issues"
22
+ },
23
+ "peerDependencies": {
24
+ "@theforeman/vendor": ">= 6.0.0"
25
+ },
26
+ "dependencies": {},
27
+ "devDependencies": {
28
+ "@babel/core": "^7.24.3",
29
+ "@sheerun/mutationobserver-shim": "^0.3.3",
30
+ "@testing-library/react": "^10.4.9",
31
+ "@theforeman/builder": "^10.1.3",
32
+ "@theforeman/eslint-plugin-foreman": "13.0.0",
33
+ "@theforeman/find-foreman": "^13.0.0",
34
+ "@theforeman/stories": "^12.2.3",
35
+ "@theforeman/test": "^13.0.0",
36
+ "@theforeman/vendor-dev": "^13.0.0",
37
+ "babel-eslint": "^10.0.3",
38
+ "eslint": "^6.7.2",
39
+ "prettier": "^1.19.1",
40
+ "react-redux-test-utils": "^0.2.0",
41
+ "stylelint": "^16.3.1",
42
+ "stylelint-config-standard": "^36.0.0"
43
+ }
44
+ }
@@ -0,0 +1,113 @@
1
+ import { addToast } from 'foremanReact/components/ToastsList';
2
+ import { translate as __ } from 'foremanReact/common/I18n';
3
+ import { get, put, post } from 'foremanReact/redux/API';
4
+
5
+ /* API constants */
6
+ const RESOURCE_QUOTA_KEY = 'RESOURCE_QUOTAS';
7
+
8
+ export const resourceQuotaKey = quotaId => {
9
+ if (quotaId) return `${RESOURCE_QUOTA_KEY}_${quotaId}`;
10
+ return `${RESOURCE_QUOTA_KEY}`;
11
+ };
12
+
13
+ /* perform an API call to list all existing Resource Quotas */
14
+ const apiListResourceQuotas = (stateCallback, componentCallback) =>
15
+ get({
16
+ key: resourceQuotaKey(),
17
+ url: `/foreman_resource_quota/api/v2/resource_quotas`,
18
+ handleSuccess: response => stateCallback(true, response, componentCallback),
19
+ handleError: response => stateCallback(false, response, componentCallback),
20
+ });
21
+
22
+ /* perform an API call to get basic information on a Resource Quota */
23
+ const apiGetResourceQuota = (quotaId, stateCallback, componentCallback) =>
24
+ get({
25
+ key: resourceQuotaKey(),
26
+ url: `/foreman_resource_quota/api/v2/resource_quotas/${quotaId}`,
27
+ handleSuccess: response => stateCallback(true, response, componentCallback),
28
+ handleError: response => stateCallback(false, response, componentCallback),
29
+ });
30
+
31
+ /* perform an API call to determine information on a Resource Quota's utilization */
32
+ const apiGetResourceQuotaUtilization = (
33
+ quotaId,
34
+ stateCallback,
35
+ componentCallback
36
+ ) =>
37
+ get({
38
+ key: resourceQuotaKey(),
39
+ url: `/foreman_resource_quota/api/v2/resource_quotas/${quotaId}/utilization`,
40
+ handleSuccess: response => stateCallback(true, response, componentCallback),
41
+ handleError: response => stateCallback(false, response, componentCallback),
42
+ });
43
+
44
+ /* perform an API call to create a new Resource Quota */
45
+ const apiCreateResourceQuota = (payload, stateCallback, componentCallback) =>
46
+ post({
47
+ key: resourceQuotaKey(),
48
+ url: `/foreman_resource_quota/api/v2/resource_quotas`,
49
+ params: { resource_quota: payload },
50
+ handleSuccess: response => stateCallback(true, response, componentCallback),
51
+ handleError: response => stateCallback(false, response, componentCallback),
52
+ });
53
+
54
+ /* perform an API call to update Resource Quota data */
55
+ const apiUpdateResourceQuota = (
56
+ quotaId,
57
+ payload,
58
+ stateCallback,
59
+ componentCallback
60
+ ) =>
61
+ put({
62
+ key: resourceQuotaKey(quotaId),
63
+ url: `/foreman_resource_quota/api/v2/resource_quotas/${quotaId}`,
64
+ params: { resource_quota: payload },
65
+ handleSuccess: response => stateCallback(true, response, componentCallback),
66
+ handleError: response => stateCallback(false, response, componentCallback),
67
+ });
68
+
69
+ /**
70
+ * Handles the callback response from an asynchronous operation, displaying a toast message accordingly.
71
+ * @param {function} dispatcher - The dispatcher function to dispatch actions.
72
+ * @param {boolean} isSuccess - Indicates whether the operation was successful or not.
73
+ * @param {object} response - The response object returned from the operation.
74
+ * @param {string} successMessage - The success message to display in case of success.
75
+ * @param {string} errorMessage - The error message to display in case of failure.
76
+ */
77
+ const dispatchAPICallbackToast = (
78
+ dispatch,
79
+ isSuccess,
80
+ response,
81
+ successMessage,
82
+ errorMessage
83
+ ) => {
84
+ if (isSuccess) {
85
+ dispatch(
86
+ addToast({
87
+ type: 'success',
88
+ message: __(successMessage),
89
+ })
90
+ );
91
+ } else {
92
+ let printErrorMessage = __(errorMessage);
93
+ /* eslint-disable-next-line camelcase */
94
+ if (response?.response?.data?.error?.full_messages) {
95
+ printErrorMessage += __(` ${response.response.data.error.full_messages}`);
96
+ }
97
+ dispatch(
98
+ addToast({
99
+ type: 'warning',
100
+ message: printErrorMessage,
101
+ })
102
+ );
103
+ }
104
+ };
105
+
106
+ export {
107
+ apiListResourceQuotas,
108
+ apiGetResourceQuota,
109
+ apiGetResourceQuotaUtilization,
110
+ apiCreateResourceQuota,
111
+ apiUpdateResourceQuota,
112
+ dispatchAPICallbackToast,
113
+ };
@@ -0,0 +1,96 @@
1
+ import { dispatchAPICallbackToast } from './api_helper';
2
+
3
+ jest.mock('foremanReact/components/ToastsList', () => ({
4
+ addToast: jest.fn(param => param),
5
+ }));
6
+
7
+ jest.mock('foremanReact/common/I18n', () => ({
8
+ translate: jest.fn(param => param),
9
+ }));
10
+
11
+ const dispatch = jest.fn();
12
+
13
+ describe('dispatchAPICallbackToast', () => {
14
+ afterEach(() => {
15
+ jest.clearAllMocks(); // Reset mock calls after each test
16
+ });
17
+
18
+ it('should dispatch a success toast', () => {
19
+ const isSuccess = true;
20
+ const response = {};
21
+ const successMessage = 'Success message';
22
+ const errorMessage = 'Error message';
23
+
24
+ dispatchAPICallbackToast(
25
+ dispatch,
26
+ isSuccess,
27
+ response,
28
+ successMessage,
29
+ errorMessage
30
+ );
31
+
32
+ expect(dispatch).toHaveBeenCalledTimes(1);
33
+ expect(dispatch).toHaveBeenCalledWith({
34
+ type: 'success',
35
+ message: 'Success message',
36
+ });
37
+ });
38
+
39
+ it('should dispatch a warning toast with error', () => {
40
+ const isSuccess = false;
41
+ const response = {
42
+ response: {
43
+ data: {
44
+ error: {
45
+ full_messages: 'Some nice error',
46
+ },
47
+ },
48
+ },
49
+ };
50
+ const successMessage = 'Success message';
51
+ const errorMessage = 'Error message';
52
+
53
+ dispatchAPICallbackToast(
54
+ dispatch,
55
+ isSuccess,
56
+ response,
57
+ successMessage,
58
+ errorMessage
59
+ );
60
+
61
+ expect(dispatch).toHaveBeenCalledTimes(1);
62
+ expect(dispatch).toHaveBeenCalledWith({
63
+ type: 'warning',
64
+ message: 'Error message Some nice error',
65
+ });
66
+ });
67
+
68
+ it('should dispatch a warning toast without error', () => {
69
+ const isSuccess = false;
70
+ const response = {
71
+ response: {
72
+ data: {
73
+ notTheExpectedErrorFormat: {
74
+ full_messages: 'Some nice error',
75
+ },
76
+ },
77
+ },
78
+ };
79
+ const successMessage = 'Success message';
80
+ const errorMessage = 'Error message';
81
+
82
+ dispatchAPICallbackToast(
83
+ dispatch,
84
+ isSuccess,
85
+ response,
86
+ successMessage,
87
+ errorMessage
88
+ );
89
+
90
+ expect(dispatch).toHaveBeenCalledTimes(1);
91
+ expect(dispatch).toHaveBeenCalledWith({
92
+ type: 'warning',
93
+ message: 'Error message',
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,46 @@
1
+ import React, { useState } from 'react';
2
+ import { Button, Modal, ModalVariant } from '@patternfly/react-core';
3
+
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+
6
+ import ResourceQuotaForm from './ResourceQuotaForm';
7
+ import { MODAL_ID_CREATE_RESOURCE_QUOTA } from './ResourceQuotaForm/ResourceQuotaFormConstants';
8
+
9
+ const CreateResourceQuotaModal = () => {
10
+ const [isOpen, setIsOpen] = useState(false);
11
+
12
+ const onSubmitSuccessCallback = success => {
13
+ if (success) {
14
+ setIsOpen(false);
15
+ window.location.reload();
16
+ }
17
+ };
18
+
19
+ return (
20
+ <div>
21
+ <Button
22
+ id="foreman-resource-quota-create-modal-button"
23
+ variant="primary"
24
+ onClick={() => {
25
+ setIsOpen(true);
26
+ }}
27
+ >
28
+ {__('Create resource quota')}
29
+ </Button>{' '}
30
+ <Modal
31
+ ouiaId={MODAL_ID_CREATE_RESOURCE_QUOTA}
32
+ title={__('Create resource quota')}
33
+ variant={ModalVariant.small}
34
+ isOpen={isOpen}
35
+ onClose={() => {
36
+ setIsOpen(false);
37
+ }}
38
+ appendTo={document.body}
39
+ >
40
+ <ResourceQuotaForm isNewQuota onSubmit={onSubmitSuccessCallback} />
41
+ </Modal>
42
+ </div>
43
+ );
44
+ };
45
+
46
+ export default CreateResourceQuotaModal;