foreman_statistics 0.1.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_statistics/api/v2/statistics_controller.rb +9 -1
  3. data/app/controllers/foreman_statistics/api/v2/trends_controller.rb +8 -0
  4. data/app/jobs/foreman_statistics/trend_counter_job.rb +23 -0
  5. data/app/views/foreman_statistics/layouts/application_react.html.erb +1 -2
  6. data/app/views/foreman_statistics/trends/_empty_data.html.erb +0 -1
  7. data/app/views/foreman_statistics/trends/index.html.erb +3 -12
  8. data/app/views/foreman_statistics/trends/welcome.html.erb +1 -1
  9. data/db/migrate/20200605153005_migrate_core_types.rb +6 -2
  10. data/lib/foreman_statistics/engine.rb +13 -1
  11. data/lib/foreman_statistics/version.rb +1 -1
  12. data/lib/tasks/foreman_statistics_tasks.rake +3 -2
  13. data/locale/action_names.rb +5 -0
  14. data/locale/en/LC_MESSAGES/foreman_statistics.mo +0 -0
  15. data/locale/en/foreman_statistics.edit.po +258 -0
  16. data/locale/en/foreman_statistics.po +181 -2
  17. data/locale/en/foreman_statistics.po.time_stamp +0 -0
  18. data/locale/foreman_statistics.pot +265 -8
  19. data/locale/gemspec.rb +1 -1
  20. data/package.json +42 -0
  21. data/test/functional/foreman_statistics/api/v2/statistics_controller_test.rb +1 -1
  22. data/test/functional/foreman_statistics/api/v2/trends_controller_test.rb +1 -1
  23. data/test/functional/foreman_statistics/statistics_controller_test.rb +5 -7
  24. data/test/functional/foreman_statistics/trends_controller_test.rb +3 -3
  25. data/test/models/foreman_statistics/trend_counter_test.rb +1 -1
  26. data/test/models/foreman_statistics/trend_test.rb +1 -1
  27. data/test/{test_plugin_helper.rb → test_statistics_helper.rb} +0 -0
  28. data/test/unit/foreman_statistics/access_permissions_test.rb +1 -1
  29. data/test/unit/foreman_statistics/statistics_test.rb +1 -1
  30. data/test/unit/foreman_statistics_test.rb +1 -1
  31. data/test/unit/tasks/foreman_statistics_tasks_test.rb +1 -1
  32. data/webpack/__mocks__/foremanReact/API.js +7 -0
  33. data/webpack/__mocks__/foremanReact/common/HOC.js +24 -0
  34. data/webpack/__mocks__/foremanReact/common/I18n.js +3 -0
  35. data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
  36. data/webpack/__mocks__/foremanReact/common/urlHelpers.js +1 -0
  37. data/webpack/__mocks__/foremanReact/components/ChartBox/index.js +2 -0
  38. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
  39. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +10 -0
  40. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +4 -0
  41. data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +2 -0
  42. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
  43. data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +5 -0
  44. data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +4 -0
  45. data/webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js +5 -0
  46. data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +3 -0
  47. data/webpack/__mocks__/foremanReact/components/common/table.js +5 -0
  48. data/webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js +7 -0
  49. data/webpack/__mocks__/foremanReact/constants.js +24 -0
  50. data/webpack/__mocks__/foremanReact/readme.md +11 -0
  51. data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +8 -0
  52. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
  53. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js +5 -0
  54. data/webpack/__mocks__/foremanReact/routes/common/reducerHOC/withDataReducer.js +35 -0
  55. data/webpack/fills_index.js +15 -0
  56. data/webpack/index.js +21 -0
  57. data/webpack/src/Components/StatisticsChartsList/StatisticsChartsList.fixtures.js +19 -0
  58. data/webpack/src/Components/StatisticsChartsList/StatisticsChartsList.test.js +18 -0
  59. data/webpack/src/Components/StatisticsChartsList/StatisticsChartsListStyles.scss +28 -0
  60. data/webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap +42 -0
  61. data/webpack/src/Components/StatisticsChartsList/index.js +32 -0
  62. data/webpack/src/ForemanStatistics.js +11 -0
  63. data/webpack/src/Router/StatisticsPage/Statistics/Statistics.js +27 -0
  64. data/webpack/src/Router/StatisticsPage/StatisticsPage.fixtures.js +11 -0
  65. data/webpack/src/Router/StatisticsPage/StatisticsPage.js +21 -0
  66. data/webpack/src/Router/StatisticsPage/StatisticsPageActions.js +43 -0
  67. data/webpack/src/Router/StatisticsPage/StatisticsPageSelectors.js +13 -0
  68. data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPage.test.js +12 -0
  69. data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPageActions.test.js +27 -0
  70. data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPageSelectors.test.js +34 -0
  71. data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPage.test.js.snap +32 -0
  72. data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap +54 -0
  73. data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap +35 -0
  74. data/webpack/src/Router/StatisticsPage/constants.js +4 -0
  75. data/webpack/src/Router/StatisticsPage/index.js +36 -0
  76. data/webpack/src/Router/__snapshots__/routes.test.js.snap +47 -0
  77. data/webpack/src/Router/index.js +14 -0
  78. data/webpack/src/Router/routes.js +11 -0
  79. data/webpack/src/Router/routes.test.js +27 -0
  80. data/webpack/src/index.js +1 -0
  81. data/webpack/src/reducers.js +9 -0
  82. data/webpack/src/trends.js +7 -0
  83. data/webpack/src/trends.test.js +44 -0
  84. metadata +67 -9
data/locale/gemspec.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  # Matches foreman_statistics.gemspec
2
- _('TODO: Description of ForemanStatistics.')
2
+ _('Statistics and Trends for Foreman gives users overview of their infrastructure.')
data/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "foreman_statistics",
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",
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_statistics.git"
19
+ },
20
+ "bugs": {
21
+ "url": "http://projects.theforeman.org/projects/foreman_statistics/issues"
22
+ },
23
+ "peerDependencies": {
24
+ "@theforeman/vendor": ">= 4.0.2"
25
+ },
26
+ "dependencies": {
27
+ "react-intl": "^2.8.0"
28
+ },
29
+ "devDependencies": {
30
+ "@babel/core": "^7.7.0",
31
+ "@theforeman/builder": "^4.0.2",
32
+ "@theforeman/eslint-plugin-foreman": "4.0.5",
33
+ "@theforeman/stories": "^4.0.2",
34
+ "@theforeman/test": "^4.0.2",
35
+ "@theforeman/vendor-dev": "^4.0.2",
36
+ "babel-eslint": "^10.0.3",
37
+ "eslint": "^6.7.2",
38
+ "prettier": "^1.13.5",
39
+ "stylelint-config-standard": "^18.0.0",
40
+ "stylelint": "^9.3.0"
41
+ }
42
+ }
@@ -1,4 +1,4 @@
1
- require_relative '../../../../test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  class ForemanStatistics::Api::V2::StatisticsControllerTest < ActionController::TestCase
4
4
  setup do
@@ -1,4 +1,4 @@
1
- require_relative '../../../../test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  class ForemanStatistics::Api::V2::TrendsControllerTest < ActionController::TestCase
4
4
  setup do
@@ -1,4 +1,4 @@
1
- require_relative '../../test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  module ForemanStatistics
4
4
  class StatisticsControllerTest < ActionController::TestCase
@@ -7,16 +7,14 @@ module ForemanStatistics
7
7
  end
8
8
 
9
9
  test 'user with viewer rights should succeed in viewing statistics' do
10
- @request.session[:user] = users(:one).id
11
- users(:one).roles = [Role.default, Role.find_by(name: 'Viewer')]
12
- get :index, session: set_session_user
10
+ setup_user('view', 'statistics')
11
+ get :index, session: set_session_user(:one)
13
12
  assert_response :success
14
13
  end
15
14
 
16
15
  test 'user with viewer rights should succeed in requesting statistics data via ajax' do
17
- @request.session[:user] = users(:one).id
18
- users(:one).roles = [Role.default, Role.find_by(name: 'Viewer')]
19
- get :show, params: { :id => 'operatingsystem', :format => 'json' }, session: set_session_user
16
+ setup_user('view', 'statistics')
17
+ get :show, params: { :id => 'operatingsystem', :format => 'json' }, session: set_session_user(:one)
20
18
  assert_response :success
21
19
  end
22
20
  end
@@ -1,4 +1,4 @@
1
- require_relative '../../test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  module ForemanStatistics
4
4
  class TrendsControllerTest < ActionController::TestCase
@@ -46,14 +46,14 @@ module ForemanStatistics
46
46
  test 'should render correct per_page value' do
47
47
  get :index, params: { per_page: entries_per_page + 1 }, session: set_session_user
48
48
  assert_response :success
49
- per_page_results = response.body.scan(/perPage":\d+/).first.gsub(/[^\d]/, '').to_i
49
+ per_page_results = response.body.scan(/perPage&quot;:(\d+)/).first.first.to_i
50
50
  assert_equal entries_per_page, per_page_results
51
51
  end
52
52
 
53
53
  test 'should render per page dropdown with correct values' do
54
54
  get :index, params: { per_page: entries_per_page + 1 }, session: set_session_user
55
55
  assert_response :success
56
- assert_not_nil response.body['perPageOptions":[5,6,10,15,25,50]']
56
+ assert_not_nil response.body['perPageOptions&quot;:[5,6,10,15,25,50]']
57
57
  end
58
58
 
59
59
  test 'sort links should include per page param' do
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  module ForemanStatistics
4
4
  class TrendCounterTest < ActiveSupport::TestCase
@@ -1,4 +1,4 @@
1
- require_relative '../../test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  module ForemanStatistics
4
4
  class TrendTest < ActiveSupport::TestCase
@@ -1,4 +1,4 @@
1
- require 'test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
  require 'unit/shared/access_permissions_test_base'
3
3
 
4
4
  # Permissions are added in AccessPermissions with lists of controllers and
@@ -1,4 +1,4 @@
1
- require_relative '../../test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  module ForemanStatistics
4
4
  class StatisticsTest < ActiveSupport::TestCase
@@ -1,4 +1,4 @@
1
- require 'test_plugin_helper'
1
+ require 'test_statistics_helper'
2
2
 
3
3
  class ForemanStatisticsTest < ActiveSupport::TestCase
4
4
  setup do
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require 'test_statistics_helper'
2
2
  require 'rake'
3
3
 
4
4
  class ForemanStatisticsTasksTest < ActiveSupport::TestCase
@@ -0,0 +1,7 @@
1
+ export default {
2
+ get: jest.fn(),
3
+ put: jest.fn(),
4
+ post: jest.fn(),
5
+ delete: jest.fn(),
6
+ patch: jest.fn(),
7
+ };
@@ -0,0 +1,24 @@
1
+ import React, { useEffect } from 'react';
2
+
3
+ export const callOnMount = callback => WrappedComponent => componentProps => {
4
+ // fires callback onMount, [] means don't listen to any props change
5
+ useEffect(() => {
6
+ callback(componentProps);
7
+ }, [componentProps]);
8
+
9
+ return <WrappedComponent {...componentProps} />;
10
+ };
11
+
12
+ export const withRenderHandler = ({
13
+ Component,
14
+ LoadingComponent = () => jest.fn(),
15
+ ErrorComponent = () => jest.fn(),
16
+ EmptyComponent = () => jest.fn(),
17
+ }) => componentProps => {
18
+ const { isLoading, hasData, hasError } = componentProps;
19
+
20
+ if (isLoading && !hasData) return <LoadingComponent {...componentProps} />;
21
+ if (hasError) return <ErrorComponent {...componentProps} />;
22
+ if (hasData) return <Component {...componentProps} />;
23
+ return <EmptyComponent {...componentProps} />;
24
+ };
@@ -0,0 +1,3 @@
1
+ export const translate = s => s;
2
+
3
+ export const documentLocale = () => 'en';
@@ -0,0 +1 @@
1
+ export const noop = Function.prototype;
@@ -0,0 +1 @@
1
+ export const getURIsearch = () => 'a=b';
@@ -0,0 +1,2 @@
1
+ const ConnectedChartBox = () => jest.fn();
2
+ export default ConnectedChartBox;
@@ -0,0 +1,2 @@
1
+ const ForemanModalActions = () => jest.fn();
2
+ export default ForemanModalActions;
@@ -0,0 +1,10 @@
1
+ const modalOpen = true;
2
+ const setModalOpen = jest.fn();
3
+ const setModalClosed = jest.fn();
4
+
5
+ export const useForemanModal = () => ({
6
+ modalOpen,
7
+ setModalOpen,
8
+ setModalClosed,
9
+ });
10
+ export default useForemanModal;
@@ -0,0 +1,4 @@
1
+ const ForemanModal = () => jest.fn();
2
+ ForemanModal.Header = () => jest.fn();
3
+ ForemanModal.Footer = () => jest.fn();
4
+ export default ForemanModal;
@@ -0,0 +1,2 @@
1
+ export const showLoading = () => null;
2
+ export const hideLoading = () => null;
@@ -0,0 +1,2 @@
1
+ const PaginationWrapper = () => jest.fn();
2
+ export default PaginationWrapper;
@@ -0,0 +1,5 @@
1
+ const EmptyState = () => jest.fn();
2
+
3
+ export const EmptyStatePattern = () => jest.fn();
4
+
5
+ export default EmptyState;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+
3
+ export const MessageBox = () => <div className="message-box-root" />;
4
+ export default MessageBox;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+
3
+ export const LongDateTime = value => <p>{value}</p>;
4
+
5
+ export default LongDateTime;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+
3
+ export default date => <p>{`${date} time ago`}</p>;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+
3
+ export const Table = () => <div className="table" />;
4
+ export const createTableReducer = jest.fn(controller => controller);
5
+ export const cellFormatter = cell => cell;
@@ -0,0 +1,7 @@
1
+ const createTableActionTypes = tableID => ({
2
+ REQUEST: `${tableID.toUpperCase()}_REQUEST`,
3
+ SUCCESS: `${tableID.toUpperCase()}_SUCCESS`,
4
+ FAILURE: `${tableID.toUpperCase()}_FAILURE`,
5
+ });
6
+
7
+ export default createTableActionTypes;
@@ -0,0 +1,24 @@
1
+ export const STATUS = {
2
+ PENDING: 'PENDING',
3
+ RESOLVED: 'RESOLVED',
4
+ ERROR: 'ERROR',
5
+ };
6
+
7
+ export const getControllerSearchProps = (
8
+ controller,
9
+ id = 'searchBar',
10
+ canCreateBookmarks = true
11
+ ) => ({
12
+ controller,
13
+ autocomplete: {
14
+ id,
15
+ searchQuery: '',
16
+ url: `${controller}/auto_complete_search`,
17
+ useKeyShortcuts: true,
18
+ },
19
+ bookmarks: {
20
+ url: '/api/bookmarks',
21
+ canCreateBookmarks,
22
+ documentationUrl: `4.1.5Searching`,
23
+ },
24
+ });
@@ -0,0 +1,11 @@
1
+ For testing components which have imported foreman-core components,
2
+ a mock file is required in this folder.
3
+
4
+ ### Example: Mocking ForemanModal component
5
+ ```js
6
+ // __mocks__/foremanReact/components/ForemanModal/index.js
7
+ const ForemanModal = () => jest.fn();
8
+ ForemanModal.Header = () => jest.fn();
9
+ ForemanModal.Footer = () => jest.fn();
10
+ export default ForemanModal;
11
+ ```
@@ -0,0 +1,8 @@
1
+ export const addToast = toast => ({
2
+ type: 'TOASTS_ADD',
3
+ payload: {
4
+ message: toast,
5
+ },
6
+ });
7
+
8
+ export default addToast;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const PageLayout = ({ children }) => <div>{children}</div>;
5
+
6
+ PageLayout.propTypes = {
7
+ children: PropTypes.node.isRequired,
8
+ };
9
+
10
+ export default PageLayout;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+
3
+ const ExportButton = () => <button>export</button>;
4
+
5
+ export default ExportButton;
@@ -0,0 +1,35 @@
1
+ import Immutable from 'seamless-immutable';
2
+
3
+ const initialState = Immutable({
4
+ isLoading: true,
5
+ hasError: false,
6
+ hasData: false,
7
+ message: { type: 'empty', text: '' },
8
+ });
9
+
10
+ const withDataReducer = controller => (
11
+ state = initialState,
12
+ { type, payload }
13
+ ) => {
14
+ switch (type) {
15
+ case `${controller}_DATA_RESOLVED`:
16
+ return state.merge({ ...payload, isLoading: false });
17
+
18
+ case `${controller}_DATA_FAILED`:
19
+ return state.merge({ ...payload, isLoading: false, hasError: true });
20
+
21
+ case `${controller}_CLEAR_ERROR`:
22
+ return state.set('hasError', false);
23
+
24
+ case `${controller}_SHOW_LOADING`:
25
+ return state.set('isLoading', true);
26
+
27
+ case `${controller}_HIDE_LOADING`:
28
+ return state.set('isLoading', false);
29
+
30
+ default:
31
+ return state;
32
+ }
33
+ };
34
+
35
+ export default withDataReducer;
@@ -0,0 +1,15 @@
1
+ // This example for extanding foreman-core's component via slot&fill
2
+
3
+ /*
4
+ import React from 'react';
5
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
6
+
7
+ addGlobalFill('slotId', 'fillId', <SomeComponent key="some-key" />, 300);
8
+
9
+ addGlobalFill(
10
+ 'slotId',
11
+ 'fillId',
12
+ { someProp: 'this is an override prop' },
13
+ 300
14
+ );
15
+ */
data/webpack/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /* eslint import/no-unresolved: [2, { ignore: [foremanReact/*] }] */
2
+ /* eslint-disable import/no-extraneous-dependencies */
3
+ /* eslint-disable import/extensions */
4
+ import componentRegistry from 'foremanReact/components/componentRegistry';
5
+ import { registerReducer } from 'foremanReact/common/MountingService';
6
+ import reducers from './src/reducers';
7
+ import ForemanStatistics from './src/ForemanStatistics';
8
+ import * as trends from './src/trends';
9
+
10
+ Object.assign(window.tfm, { trends });
11
+
12
+ // register reducers
13
+ Object.entries(reducers).forEach(([key, reducer]) =>
14
+ registerReducer(key, reducer)
15
+ );
16
+
17
+ // register components for erb mounting
18
+ componentRegistry.register({
19
+ name: 'ForemanStatistics',
20
+ type: ForemanStatistics,
21
+ });
@@ -0,0 +1,19 @@
1
+ export const statisticsData = {
2
+ operatingsystem: {
3
+ id: 'operatingsystem',
4
+ title: 'OS Distribution',
5
+ url: 'statistics/operatingsystem',
6
+ search: '/hosts?search=os_title=~VAL~',
7
+ },
8
+ architecture: {
9
+ id: 'architecture',
10
+ title: 'Architecture Distribution',
11
+ url: 'statistics/architecture',
12
+ search: '/hosts?search=facts.architecture=~VAL~',
13
+ },
14
+ };
15
+
16
+ export const statisticsMeta = [
17
+ statisticsData.operatingsystem,
18
+ statisticsData.architecture,
19
+ ];