foreman_statistics 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/foreman_statistics/api/v2/statistics_controller.rb +9 -1
- data/app/controllers/foreman_statistics/api/v2/trends_controller.rb +8 -0
- data/db/migrate/20200605153005_migrate_core_types.rb +6 -2
- data/lib/foreman_statistics/engine.rb +1 -1
- data/lib/foreman_statistics/version.rb +1 -1
- data/webpack/__mocks__/foremanReact/API.js +7 -0
- data/webpack/__mocks__/foremanReact/common/HOC.js +24 -0
- data/webpack/__mocks__/foremanReact/common/I18n.js +3 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/common/urlHelpers.js +1 -0
- data/webpack/__mocks__/foremanReact/components/ChartBox/index.js +2 -0
- data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
- data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +10 -0
- data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +4 -0
- data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +2 -0
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
- data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +4 -0
- data/webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +3 -0
- data/webpack/__mocks__/foremanReact/components/common/table.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js +7 -0
- data/webpack/__mocks__/foremanReact/constants.js +24 -0
- data/webpack/__mocks__/foremanReact/readme.md +11 -0
- data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +8 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js +5 -0
- data/webpack/__mocks__/foremanReact/routes/common/reducerHOC/withDataReducer.js +35 -0
- data/webpack/fills_index.js +15 -0
- data/webpack/index.js +21 -0
- data/webpack/src/Components/StatisticsChartsList/StatisticsChartsList.fixtures.js +19 -0
- data/webpack/src/Components/StatisticsChartsList/StatisticsChartsList.test.js +18 -0
- data/webpack/src/Components/StatisticsChartsList/StatisticsChartsListStyles.scss +28 -0
- data/webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap +42 -0
- data/webpack/src/Components/StatisticsChartsList/index.js +32 -0
- data/webpack/src/ForemanStatistics.js +11 -0
- data/webpack/src/Router/StatisticsPage/Statistics/Statistics.js +27 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPage.fixtures.js +11 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPage.js +21 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPageActions.js +43 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPageSelectors.js +12 -0
- data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPage.test.js +12 -0
- data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPageActions.test.js +27 -0
- data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPageSelectors.test.js +32 -0
- data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPage.test.js.snap +32 -0
- data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap +54 -0
- data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap +35 -0
- data/webpack/src/Router/StatisticsPage/constants.js +4 -0
- data/webpack/src/Router/StatisticsPage/index.js +36 -0
- data/webpack/src/Router/__snapshots__/routes.test.js.snap +47 -0
- data/webpack/src/Router/index.js +14 -0
- data/webpack/src/Router/routes.js +11 -0
- data/webpack/src/Router/routes.test.js +27 -0
- data/webpack/src/index.js +1 -0
- data/webpack/src/reducers.js +7 -0
- data/webpack/src/trends.js +7 -0
- data/webpack/src/trends.test.js +44 -0
- metadata +58 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2535d7ffe5c9d9e7d594d61a1ed6e5fbed330c3b5739a3792ce78910c365ef2
|
4
|
+
data.tar.gz: 8be508f7afacb9d168013e7d5a101e57dd775107f4d3aa12211b252d5a3bfe94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1849bc62e6be28e6572727e87afaeba9c9e803643d0de003a0d903a81ba3b5a6a964755a1ebc734054ab1e09cd5116135b34d5392a2eb41e653d956dac1b9973
|
7
|
+
data.tar.gz: 0fa5e7507dcc8d5e75755cdbd8fd7e70195e1bc1fa37c037c0d00740da2771446fec24b49dbbed50dfe4b7d6e7d45a7c6386e5261cd5329e49bffac0808f8946
|
@@ -2,13 +2,14 @@ module ForemanStatistics
|
|
2
2
|
module Api
|
3
3
|
module V2
|
4
4
|
class StatisticsController < ::Api::V2::BaseController
|
5
|
+
before_action :show_deprecation_for_core_routes
|
6
|
+
|
5
7
|
resource_description do
|
6
8
|
api_version 'v2'
|
7
9
|
api_base_url '/foreman_statistics/api'
|
8
10
|
end
|
9
11
|
|
10
12
|
api :GET, '/statistics/', N_('Get statistics')
|
11
|
-
|
12
13
|
def index
|
13
14
|
@os_count = Host.authorized(:view_hosts).count_distribution :operatingsystem
|
14
15
|
@arch_count = Host.authorized(:view_hosts).count_distribution :architecture
|
@@ -27,6 +28,13 @@ module ForemanStatistics
|
|
27
28
|
:model_count => @model_count, :mem_size => @mem_size, :mem_free => @mem_free,
|
28
29
|
:swap_free => @swap_free, :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree }
|
29
30
|
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def show_deprecation_for_core_routes
|
35
|
+
return if request.path.starts_with?('/foreman_statistics')
|
36
|
+
Foreman::Deprecation.api_deprecation_warning('/api/v2/statistics API endpoint is deprecated, please use /foreman_statistics/api/v2/statistics instead')
|
37
|
+
end
|
30
38
|
end
|
31
39
|
end
|
32
40
|
end
|
@@ -4,6 +4,7 @@ module ForemanStatistics
|
|
4
4
|
class TrendsController < ::Api::V2::BaseController
|
5
5
|
include ForemanStatistics::Parameters::Trend
|
6
6
|
|
7
|
+
before_action :show_deprecation_for_core_routes
|
7
8
|
before_action :find_resource, :only => %i[show destroy]
|
8
9
|
|
9
10
|
TRENDABLE_TYPES = %w[
|
@@ -52,6 +53,13 @@ module ForemanStatistics
|
|
52
53
|
def resource_scope(options = {})
|
53
54
|
@resource_scope ||= scope_for(ForemanStatistics::Trend.types, options)
|
54
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def show_deprecation_for_core_routes
|
60
|
+
return if request.path.starts_with?('/foreman_statistics')
|
61
|
+
Foreman::Deprecation.api_deprecation_warning('/api/v2/trends API endpoints are deprecated, please use /foreman_statistics/api/v2/trends instead')
|
62
|
+
end
|
55
63
|
end
|
56
64
|
end
|
57
65
|
end
|
@@ -1,15 +1,19 @@
|
|
1
1
|
class MigrateCoreTypes < ActiveRecord::Migration[6.0]
|
2
|
+
class FakeTrend < ApplicationRecord
|
3
|
+
self.table_name = 'trends'
|
4
|
+
end
|
5
|
+
|
2
6
|
def up
|
3
7
|
Permission.where(:resource_type => 'Trend').update_all(:resource_type => 'ForemanStatistics::Trend')
|
4
8
|
%w[ForemanTrend FactTrend Trend].each do |t|
|
5
|
-
|
9
|
+
FakeTrend.where(:type => t).update_all(:type => "ForemanStatistics::#{t}")
|
6
10
|
end
|
7
11
|
end
|
8
12
|
|
9
13
|
def down
|
10
14
|
Permission.where(:resource_type => 'ForemanStatistics::Trend').update_all(:resource_type => 'Trend')
|
11
15
|
%w[ForemanTrend FactTrend Trend].each do |t|
|
12
|
-
|
16
|
+
FakeTrend.where(:type => "ForemanStatistics::#{t}").update_all(:type => t)
|
13
17
|
end
|
14
18
|
end
|
15
19
|
end
|
@@ -8,7 +8,7 @@ module ForemanStatistics
|
|
8
8
|
config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
|
9
9
|
config.autoload_paths += Dir["#{config.root}/app/overrides"]
|
10
10
|
|
11
|
-
config.paths['db/migrate'] << 'db/migrate_foreman'
|
11
|
+
config.paths['db/migrate'] << 'db/migrate_foreman' unless Gem::Version.new(SETTINGS[:version]).release < Gem::Version.new('2.2')
|
12
12
|
|
13
13
|
# Add any db migrations
|
14
14
|
initializer 'foreman_statistics.load_app_instance_data' do |app|
|
@@ -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 @@
|
|
1
|
+
export const noop = Function.prototype;
|
@@ -0,0 +1 @@
|
|
1
|
+
export const getURIsearch = () => 'a=b';
|
@@ -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,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
|
+
];
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
|
2
|
+
|
3
|
+
import StatisticsChartsList from './';
|
4
|
+
import { statisticsData } from './StatisticsChartsList.fixtures';
|
5
|
+
|
6
|
+
const fixtures = {
|
7
|
+
'should render no panels for empty data': {
|
8
|
+
data: {},
|
9
|
+
},
|
10
|
+
'should render two panels for fixtures data': {
|
11
|
+
data: statisticsData,
|
12
|
+
},
|
13
|
+
};
|
14
|
+
|
15
|
+
describe('StatisticsChartsList', () => {
|
16
|
+
describe('rendering', () =>
|
17
|
+
testComponentSnapshotsWithFixtures(StatisticsChartsList, fixtures));
|
18
|
+
});
|
@@ -0,0 +1,28 @@
|
|
1
|
+
.statistics-charts-list-root {
|
2
|
+
display: flex;
|
3
|
+
flex-wrap: wrap;
|
4
|
+
justify-content: space-around;
|
5
|
+
padding-top: 20px;
|
6
|
+
.chart-box {
|
7
|
+
height: 312px;
|
8
|
+
width: 280px;
|
9
|
+
display: -ms-flexbox;
|
10
|
+
display: flex;
|
11
|
+
-ms-flex-direction: column;
|
12
|
+
flex-direction: column;
|
13
|
+
min-height: 312px;
|
14
|
+
|
15
|
+
.card-pf-heading {
|
16
|
+
-ms-flex: 0 0 30px;
|
17
|
+
flex: 0 0 30px;
|
18
|
+
}
|
19
|
+
|
20
|
+
.card-pf-body {
|
21
|
+
display: -ms-flexbox;
|
22
|
+
display: flex;
|
23
|
+
-ms-flex: 1;
|
24
|
+
flex: 1;
|
25
|
+
margin: 0;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
data/webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`StatisticsChartsList rendering should render no panels for empty data 1`] = `
|
4
|
+
<div
|
5
|
+
className="statistics-charts-list-root cards-pf"
|
6
|
+
/>
|
7
|
+
`;
|
8
|
+
|
9
|
+
exports[`StatisticsChartsList rendering should render two panels for fixtures data 1`] = `
|
10
|
+
<div
|
11
|
+
className="statistics-charts-list-root cards-pf"
|
12
|
+
>
|
13
|
+
<ConnectedChartBox
|
14
|
+
chart={
|
15
|
+
Object {
|
16
|
+
"id": "operatingsystem",
|
17
|
+
"search": "/hosts?search=os_title=~VAL~",
|
18
|
+
"title": "OS Distribution",
|
19
|
+
"url": "statistics/operatingsystem",
|
20
|
+
}
|
21
|
+
}
|
22
|
+
key="operatingsystem"
|
23
|
+
noDataMsg="No data available"
|
24
|
+
tip="Expand the chart"
|
25
|
+
type="donut"
|
26
|
+
/>
|
27
|
+
<ConnectedChartBox
|
28
|
+
chart={
|
29
|
+
Object {
|
30
|
+
"id": "architecture",
|
31
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
32
|
+
"title": "Architecture Distribution",
|
33
|
+
"url": "statistics/architecture",
|
34
|
+
}
|
35
|
+
}
|
36
|
+
key="architecture"
|
37
|
+
noDataMsg="No data available"
|
38
|
+
tip="Expand the chart"
|
39
|
+
type="donut"
|
40
|
+
/>
|
41
|
+
</div>
|
42
|
+
`;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import ConnectedChartBox from 'foremanReact/components/ChartBox';
|
6
|
+
import './StatisticsChartsListStyles.scss';
|
7
|
+
|
8
|
+
const StatisticsChartsList = ({ data }) => {
|
9
|
+
const chartBoxes = Object.values(data).map(chart => (
|
10
|
+
<ConnectedChartBox
|
11
|
+
key={chart.id}
|
12
|
+
type="donut"
|
13
|
+
chart={chart}
|
14
|
+
noDataMsg={__('No data available')}
|
15
|
+
tip={__('Expand the chart')}
|
16
|
+
/>
|
17
|
+
));
|
18
|
+
|
19
|
+
return (
|
20
|
+
<div className="statistics-charts-list-root cards-pf">{chartBoxes}</div>
|
21
|
+
);
|
22
|
+
};
|
23
|
+
|
24
|
+
StatisticsChartsList.propTypes = {
|
25
|
+
data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
26
|
+
};
|
27
|
+
|
28
|
+
StatisticsChartsList.defaultProps = {
|
29
|
+
data: [],
|
30
|
+
};
|
31
|
+
|
32
|
+
export default StatisticsChartsList;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { BrowserRouter } from 'react-router-dom';
|
3
|
+
import ForemanStatisticsRoute from './Router';
|
4
|
+
|
5
|
+
const ForemanStatistics = () => (
|
6
|
+
<BrowserRouter>
|
7
|
+
<ForemanStatisticsRoute />
|
8
|
+
</BrowserRouter>
|
9
|
+
);
|
10
|
+
|
11
|
+
export default ForemanStatistics;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { withRenderHandler } from 'foremanReact/common/HOC';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import { EmptyStatePattern } from 'foremanReact/components/common/EmptyState';
|
6
|
+
import StatisticsChartsList from '../../../Components/StatisticsChartsList';
|
7
|
+
|
8
|
+
const Statistics = ({ statisticsMeta }) => {
|
9
|
+
if (!statisticsMeta) {
|
10
|
+
return (
|
11
|
+
<EmptyStatePattern
|
12
|
+
icon="info"
|
13
|
+
header={__('No Charts To Load')}
|
14
|
+
description=""
|
15
|
+
/>
|
16
|
+
);
|
17
|
+
}
|
18
|
+
return <StatisticsChartsList data={statisticsMeta} />;
|
19
|
+
};
|
20
|
+
|
21
|
+
Statistics.propTypes = {
|
22
|
+
statisticsMeta: PropTypes.array.isRequired,
|
23
|
+
};
|
24
|
+
|
25
|
+
export default withRenderHandler({
|
26
|
+
Component: Statistics,
|
27
|
+
});
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { noop } from 'foremanReact/common/helpers';
|
2
|
+
import { statisticsMeta } from '../../Components/StatisticsChartsList/StatisticsChartsList.fixtures';
|
3
|
+
|
4
|
+
export const statisticsProps = {
|
5
|
+
statisticsMeta,
|
6
|
+
isLoading: false,
|
7
|
+
hasData: true,
|
8
|
+
hasError: false,
|
9
|
+
message: {},
|
10
|
+
getStatisticsMeta: noop,
|
11
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
5
|
+
import Statistics from './Statistics/Statistics';
|
6
|
+
|
7
|
+
const StatisticsPage = ({ statisticsMeta, ...props }) => (
|
8
|
+
<PageLayout header={__('Statistics')} searchable={false}>
|
9
|
+
<Statistics statisticsMeta={statisticsMeta} {...props} />
|
10
|
+
</PageLayout>
|
11
|
+
);
|
12
|
+
|
13
|
+
StatisticsPage.propTypes = {
|
14
|
+
statisticsMeta: PropTypes.array,
|
15
|
+
};
|
16
|
+
|
17
|
+
StatisticsPage.defaultProps = {
|
18
|
+
statisticsMeta: [],
|
19
|
+
};
|
20
|
+
|
21
|
+
export default StatisticsPage;
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import API from 'foremanReact/API';
|
2
|
+
|
3
|
+
import {
|
4
|
+
STATISTICS_PAGE_DATA_RESOLVED,
|
5
|
+
STATISTICS_PAGE_DATA_FAILED,
|
6
|
+
STATISTICS_PAGE_HIDE_LOADING,
|
7
|
+
STATISTICS_PAGE_URL,
|
8
|
+
} from './constants';
|
9
|
+
|
10
|
+
export const getStatisticsMeta = (
|
11
|
+
url = STATISTICS_PAGE_URL
|
12
|
+
) => async dispatch => {
|
13
|
+
const onFetchSuccess = ({ data }) => {
|
14
|
+
dispatch(hideLoading());
|
15
|
+
dispatch({
|
16
|
+
type: STATISTICS_PAGE_DATA_RESOLVED,
|
17
|
+
payload: { metadata: data.charts, hasData: data.charts.length > 0 },
|
18
|
+
});
|
19
|
+
};
|
20
|
+
|
21
|
+
const onFetchError = ({ message }) => {
|
22
|
+
dispatch(hideLoading());
|
23
|
+
dispatch({
|
24
|
+
type: STATISTICS_PAGE_DATA_FAILED,
|
25
|
+
payload: {
|
26
|
+
message: {
|
27
|
+
type: 'error',
|
28
|
+
text: message,
|
29
|
+
},
|
30
|
+
},
|
31
|
+
});
|
32
|
+
};
|
33
|
+
try {
|
34
|
+
const response = await API.get(url);
|
35
|
+
return onFetchSuccess(response);
|
36
|
+
} catch (error) {
|
37
|
+
return onFetchError(error);
|
38
|
+
}
|
39
|
+
};
|
40
|
+
|
41
|
+
const hideLoading = () => ({
|
42
|
+
type: STATISTICS_PAGE_HIDE_LOADING,
|
43
|
+
});
|
@@ -0,0 +1,12 @@
|
|
1
|
+
export const selectStatisticsPage = state => state.statisticsPage;
|
2
|
+
|
3
|
+
export const selectStatisticsMetadata = state =>
|
4
|
+
selectStatisticsPage(state).metadata;
|
5
|
+
export const selectStatisticsIsLoading = state =>
|
6
|
+
selectStatisticsPage(state).isLoading;
|
7
|
+
export const selectStatisticsMessage = state =>
|
8
|
+
selectStatisticsPage(state).message;
|
9
|
+
export const selectStatisticsHasError = state =>
|
10
|
+
selectStatisticsPage(state).hasError;
|
11
|
+
export const selectStatisticsHasMetadata = state =>
|
12
|
+
selectStatisticsPage(state).hasData;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
|
2
|
+
import { statisticsProps } from '../StatisticsPage.fixtures';
|
3
|
+
import StatisticsPage from '../StatisticsPage';
|
4
|
+
|
5
|
+
const fixtures = {
|
6
|
+
'render with props': statisticsProps,
|
7
|
+
};
|
8
|
+
|
9
|
+
describe('StatisticsPage', () => {
|
10
|
+
describe('rendering', () =>
|
11
|
+
testComponentSnapshotsWithFixtures(StatisticsPage, fixtures));
|
12
|
+
});
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import API from 'foremanReact/API';
|
2
|
+
|
3
|
+
import { testActionSnapshotWithFixtures } from '@theforeman/test';
|
4
|
+
import { getStatisticsMeta } from '../StatisticsPageActions';
|
5
|
+
import { statisticsProps } from '../StatisticsPage.fixtures';
|
6
|
+
|
7
|
+
jest.mock('foremanReact/API');
|
8
|
+
|
9
|
+
const runStatisticsAction = (callback, props, serverMock) => {
|
10
|
+
API.get.mockImplementation(serverMock);
|
11
|
+
|
12
|
+
return callback(props);
|
13
|
+
};
|
14
|
+
|
15
|
+
const fixtures = {
|
16
|
+
'should fetch statisticsMeta': () =>
|
17
|
+
runStatisticsAction(getStatisticsMeta, {}, async () => ({
|
18
|
+
data: { charts: statisticsProps.statisticsMeta },
|
19
|
+
})),
|
20
|
+
'should fetch statisticsMeta and fail': () =>
|
21
|
+
runStatisticsAction(getStatisticsMeta, {}, async () => {
|
22
|
+
throw new Error('some-error');
|
23
|
+
}),
|
24
|
+
};
|
25
|
+
|
26
|
+
describe('StatisticsPage actions', () =>
|
27
|
+
testActionSnapshotWithFixtures(fixtures));
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
|
2
|
+
import {
|
3
|
+
selectStatisticsPage,
|
4
|
+
selectStatisticsMetadata,
|
5
|
+
selectStatisticsHasMetadata,
|
6
|
+
selectStatisticsIsLoading,
|
7
|
+
selectStatisticsMessage,
|
8
|
+
selectStatisticsHasError,
|
9
|
+
} from '../StatisticsPageSelectors';
|
10
|
+
import { statisticsProps } from '../StatisticsPage.fixtures';
|
11
|
+
|
12
|
+
const state = {
|
13
|
+
statisticsPage: {
|
14
|
+
...statisticsProps,
|
15
|
+
},
|
16
|
+
};
|
17
|
+
|
18
|
+
const fixtures = {
|
19
|
+
'should return StatisticsPage': () => selectStatisticsPage(state),
|
20
|
+
'should return StatisticsHasMetadata': () =>
|
21
|
+
selectStatisticsHasMetadata(state),
|
22
|
+
'should return StatisticsPage statisticsMeta': () =>
|
23
|
+
selectStatisticsMetadata(state),
|
24
|
+
'should return StatisticsPage isLoading': () =>
|
25
|
+
selectStatisticsIsLoading(state),
|
26
|
+
'should return StatisticsPage Message': () => selectStatisticsMessage(state),
|
27
|
+
'should return StatisticsPage hasError': () =>
|
28
|
+
selectStatisticsHasError(state),
|
29
|
+
};
|
30
|
+
|
31
|
+
describe('StatisticsPage selectors', () =>
|
32
|
+
testSelectorsSnapshotWithFixtures(fixtures));
|
@@ -0,0 +1,32 @@
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`StatisticsPage rendering render with props 1`] = `
|
4
|
+
<PageLayout
|
5
|
+
header="Statistics"
|
6
|
+
searchable={false}
|
7
|
+
>
|
8
|
+
<Component
|
9
|
+
getStatisticsMeta={[Function]}
|
10
|
+
hasData={true}
|
11
|
+
hasError={false}
|
12
|
+
isLoading={false}
|
13
|
+
message={Object {}}
|
14
|
+
statisticsMeta={
|
15
|
+
Array [
|
16
|
+
Object {
|
17
|
+
"id": "operatingsystem",
|
18
|
+
"search": "/hosts?search=os_title=~VAL~",
|
19
|
+
"title": "OS Distribution",
|
20
|
+
"url": "statistics/operatingsystem",
|
21
|
+
},
|
22
|
+
Object {
|
23
|
+
"id": "architecture",
|
24
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
25
|
+
"title": "Architecture Distribution",
|
26
|
+
"url": "statistics/architecture",
|
27
|
+
},
|
28
|
+
]
|
29
|
+
}
|
30
|
+
/>
|
31
|
+
</PageLayout>
|
32
|
+
`;
|
data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`StatisticsPage actions should fetch statisticsMeta 1`] = `
|
4
|
+
Array [
|
5
|
+
Array [
|
6
|
+
Object {
|
7
|
+
"type": "STATISTICS_PAGE_HIDE_LOADING",
|
8
|
+
},
|
9
|
+
],
|
10
|
+
Array [
|
11
|
+
Object {
|
12
|
+
"payload": Object {
|
13
|
+
"hasData": true,
|
14
|
+
"metadata": Array [
|
15
|
+
Object {
|
16
|
+
"id": "operatingsystem",
|
17
|
+
"search": "/hosts?search=os_title=~VAL~",
|
18
|
+
"title": "OS Distribution",
|
19
|
+
"url": "statistics/operatingsystem",
|
20
|
+
},
|
21
|
+
Object {
|
22
|
+
"id": "architecture",
|
23
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
24
|
+
"title": "Architecture Distribution",
|
25
|
+
"url": "statistics/architecture",
|
26
|
+
},
|
27
|
+
],
|
28
|
+
},
|
29
|
+
"type": "STATISTICS_PAGE_DATA_RESOLVED",
|
30
|
+
},
|
31
|
+
],
|
32
|
+
]
|
33
|
+
`;
|
34
|
+
|
35
|
+
exports[`StatisticsPage actions should fetch statisticsMeta and fail 1`] = `
|
36
|
+
Array [
|
37
|
+
Array [
|
38
|
+
Object {
|
39
|
+
"type": "STATISTICS_PAGE_HIDE_LOADING",
|
40
|
+
},
|
41
|
+
],
|
42
|
+
Array [
|
43
|
+
Object {
|
44
|
+
"payload": Object {
|
45
|
+
"message": Object {
|
46
|
+
"text": "some-error",
|
47
|
+
"type": "error",
|
48
|
+
},
|
49
|
+
},
|
50
|
+
"type": "STATISTICS_PAGE_FETCH_FAILED",
|
51
|
+
},
|
52
|
+
],
|
53
|
+
]
|
54
|
+
`;
|
data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`StatisticsPage selectors should return StatisticsHasMetadata 1`] = `true`;
|
4
|
+
|
5
|
+
exports[`StatisticsPage selectors should return StatisticsPage 1`] = `
|
6
|
+
Object {
|
7
|
+
"getStatisticsMeta": [Function],
|
8
|
+
"hasData": true,
|
9
|
+
"hasError": false,
|
10
|
+
"isLoading": false,
|
11
|
+
"message": Object {},
|
12
|
+
"statisticsMeta": Array [
|
13
|
+
Object {
|
14
|
+
"id": "operatingsystem",
|
15
|
+
"search": "/hosts?search=os_title=~VAL~",
|
16
|
+
"title": "OS Distribution",
|
17
|
+
"url": "statistics/operatingsystem",
|
18
|
+
},
|
19
|
+
Object {
|
20
|
+
"id": "architecture",
|
21
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
22
|
+
"title": "Architecture Distribution",
|
23
|
+
"url": "statistics/architecture",
|
24
|
+
},
|
25
|
+
],
|
26
|
+
}
|
27
|
+
`;
|
28
|
+
|
29
|
+
exports[`StatisticsPage selectors should return StatisticsPage Message 1`] = `Object {}`;
|
30
|
+
|
31
|
+
exports[`StatisticsPage selectors should return StatisticsPage hasError 1`] = `false`;
|
32
|
+
|
33
|
+
exports[`StatisticsPage selectors should return StatisticsPage isLoading 1`] = `false`;
|
34
|
+
|
35
|
+
exports[`StatisticsPage selectors should return StatisticsPage statisticsMeta 1`] = `undefined`;
|
@@ -0,0 +1,4 @@
|
|
1
|
+
export const STATISTICS_PAGE_DATA_RESOLVED = 'STATISTICS_PAGE_DATA_RESOLVED';
|
2
|
+
export const STATISTICS_PAGE_DATA_FAILED = 'STATISTICS_PAGE_FETCH_FAILED';
|
3
|
+
export const STATISTICS_PAGE_HIDE_LOADING = 'STATISTICS_PAGE_HIDE_LOADING';
|
4
|
+
export const STATISTICS_PAGE_URL = '/foreman_statistics/statistics';
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { compose, bindActionCreators } from 'redux';
|
2
|
+
import { connect } from 'react-redux';
|
3
|
+
import { callOnMount } from 'foremanReact/common/HOC';
|
4
|
+
import withDataReducer from 'foremanReact/routes/common/reducerHOC/withDataReducer';
|
5
|
+
|
6
|
+
import * as actions from './StatisticsPageActions';
|
7
|
+
import {
|
8
|
+
selectStatisticsMetadata,
|
9
|
+
selectStatisticsMessage,
|
10
|
+
selectStatisticsIsLoading,
|
11
|
+
selectStatisticsHasMetadata,
|
12
|
+
selectStatisticsHasError,
|
13
|
+
} from './StatisticsPageSelectors';
|
14
|
+
|
15
|
+
import StatisticsPage from './StatisticsPage';
|
16
|
+
|
17
|
+
// map state to props
|
18
|
+
const mapStateToProps = state => ({
|
19
|
+
statisticsMeta: selectStatisticsMetadata(state),
|
20
|
+
isLoading: selectStatisticsIsLoading(state),
|
21
|
+
message: selectStatisticsMessage(state),
|
22
|
+
hasData: selectStatisticsHasMetadata(state),
|
23
|
+
hasError: selectStatisticsHasError(state),
|
24
|
+
});
|
25
|
+
|
26
|
+
// map action dispatchers to props
|
27
|
+
const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
|
28
|
+
|
29
|
+
// export reducers
|
30
|
+
export const reducers = { statisticsPage: withDataReducer('STATISTICS_PAGE') };
|
31
|
+
|
32
|
+
// export connected component
|
33
|
+
export default compose(
|
34
|
+
connect(mapStateToProps, mapDispatchToProps),
|
35
|
+
callOnMount(({ getStatisticsMeta }) => getStatisticsMeta())
|
36
|
+
)(StatisticsPage);
|
@@ -0,0 +1,47 @@
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`ForemanPluginTemplateRoutes should create routes 1`] = `
|
4
|
+
Object {
|
5
|
+
"statistics": Object {
|
6
|
+
"component": Object {
|
7
|
+
"$$typeof": Symbol(react.memo),
|
8
|
+
"WrappedComponent": [Function],
|
9
|
+
"compare": null,
|
10
|
+
"displayName": "Connect(Component)",
|
11
|
+
"type": [Function],
|
12
|
+
},
|
13
|
+
"exact": true,
|
14
|
+
"path": "/foreman_statistics/statistics",
|
15
|
+
"renderResult": <ContextProvider
|
16
|
+
value={
|
17
|
+
Object {
|
18
|
+
"store": Object {
|
19
|
+
"dispatch": [MockFunction],
|
20
|
+
"getState": [Function],
|
21
|
+
"subscribe": [MockFunction],
|
22
|
+
},
|
23
|
+
"subscription": Subscription {
|
24
|
+
"handleChangeWrapper": [Function],
|
25
|
+
"listeners": Object {
|
26
|
+
"notify": [Function],
|
27
|
+
},
|
28
|
+
"onStateChange": [Function],
|
29
|
+
"parentSub": undefined,
|
30
|
+
"store": Object {
|
31
|
+
"dispatch": [MockFunction],
|
32
|
+
"getState": [Function],
|
33
|
+
"subscribe": [MockFunction],
|
34
|
+
},
|
35
|
+
"unsubscribe": null,
|
36
|
+
},
|
37
|
+
}
|
38
|
+
}
|
39
|
+
>
|
40
|
+
<Connect(Component)
|
41
|
+
history={Object {}}
|
42
|
+
some="props"
|
43
|
+
/>
|
44
|
+
</ContextProvider>,
|
45
|
+
},
|
46
|
+
}
|
47
|
+
`;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Switch, Route } from 'react-router-dom';
|
3
|
+
|
4
|
+
import routes from './routes';
|
5
|
+
|
6
|
+
const Router = () => (
|
7
|
+
<Switch>
|
8
|
+
{Object.entries(routes).map(([key, props]) => (
|
9
|
+
<Route key={key} {...props} />
|
10
|
+
))}
|
11
|
+
</Switch>
|
12
|
+
);
|
13
|
+
|
14
|
+
export default Router;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Provider } from 'react-redux';
|
3
|
+
import { shallow } from '@theforeman/test';
|
4
|
+
|
5
|
+
import { statisticsProps } from './StatisticsPage/StatisticsPage.fixtures';
|
6
|
+
import Routes from './routes';
|
7
|
+
|
8
|
+
describe('ForemanPluginTemplateRoutes', () => {
|
9
|
+
it('should create routes', () => {
|
10
|
+
Object.entries(Routes).forEach(([key, Route]) => {
|
11
|
+
const store = {
|
12
|
+
subscribe: jest.fn(),
|
13
|
+
dispatch: jest.fn(),
|
14
|
+
getState: () => statisticsProps,
|
15
|
+
};
|
16
|
+
const RouteComponent = Route.component;
|
17
|
+
const component = shallow(
|
18
|
+
<Provider store={store}>
|
19
|
+
<RouteComponent history={{}} some="props" />
|
20
|
+
</Provider>
|
21
|
+
);
|
22
|
+
Route.renderResult = component;
|
23
|
+
});
|
24
|
+
|
25
|
+
expect(Routes).toMatchSnapshot();
|
26
|
+
});
|
27
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default } from './ForemanStatistics';
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/* eslint-disable jquery/no-val */
|
2
|
+
/* eslint-disable jquery/no-is */
|
3
|
+
|
4
|
+
import $ from 'jquery';
|
5
|
+
|
6
|
+
jest.unmock('jquery');
|
7
|
+
jest.unmock('./trends');
|
8
|
+
window.trends = require('./trends');
|
9
|
+
|
10
|
+
describe('selecting trend type', () => {
|
11
|
+
it('should disable fields on non-fact trend', () => {
|
12
|
+
document.body.innerHTML = `<select id="trendable_type" onchange="trends.trendTypeSelected(this)">
|
13
|
+
<option value="FactName">Facts</option>
|
14
|
+
<option value="Hostgroup">Host group</option>
|
15
|
+
</select>
|
16
|
+
<select id="trend_trendable_id">
|
17
|
+
<option value=""></option>
|
18
|
+
<option value="27">architecture</option>
|
19
|
+
</select>
|
20
|
+
<input id="trend_name">`;
|
21
|
+
$('#trendable_type')
|
22
|
+
.val('Hostgroup')
|
23
|
+
.change();
|
24
|
+
expect($('#trend_trendable_id').is(':disabled')).toBeTruthy();
|
25
|
+
expect($('#trend_name').is(':disabled')).toBeTruthy();
|
26
|
+
});
|
27
|
+
|
28
|
+
it('should enable fields on non-fact trend', () => {
|
29
|
+
document.body.innerHTML = `<select id="trendable_type" onchange="trends.trendTypeSelected(this)">
|
30
|
+
<option value="Evironment">Environment</option>
|
31
|
+
<option value="FactName">Facts</option>
|
32
|
+
</select>
|
33
|
+
<select id="trend_trendable_id" disabled>
|
34
|
+
<option value=""></option>
|
35
|
+
<option value="27">architecture</option>
|
36
|
+
</select>
|
37
|
+
<input id="trend_name" disabled>`;
|
38
|
+
$('#trendable_type')
|
39
|
+
.val('FactName')
|
40
|
+
.change();
|
41
|
+
expect($('#trend_trendable_id').is(':disabled')).toBeFalsy();
|
42
|
+
expect($('#trend_name').is(':disabled')).toBeFalsy();
|
43
|
+
});
|
44
|
+
});
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_statistics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ondrej Ezr
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rubocop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
33
|
+
version: '0.83'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
40
|
+
version: '0.83'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rubocop-minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,6 +159,58 @@ files:
|
|
159
159
|
- test/unit/foreman_statistics/statistics_test.rb
|
160
160
|
- test/unit/foreman_statistics_test.rb
|
161
161
|
- test/unit/tasks/foreman_statistics_tasks_test.rb
|
162
|
+
- webpack/__mocks__/foremanReact/API.js
|
163
|
+
- webpack/__mocks__/foremanReact/common/HOC.js
|
164
|
+
- webpack/__mocks__/foremanReact/common/I18n.js
|
165
|
+
- webpack/__mocks__/foremanReact/common/helpers.js
|
166
|
+
- webpack/__mocks__/foremanReact/common/urlHelpers.js
|
167
|
+
- webpack/__mocks__/foremanReact/components/ChartBox/index.js
|
168
|
+
- webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js
|
169
|
+
- webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js
|
170
|
+
- webpack/__mocks__/foremanReact/components/ForemanModal/index.js
|
171
|
+
- webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js
|
172
|
+
- webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js
|
173
|
+
- webpack/__mocks__/foremanReact/components/common/EmptyState.js
|
174
|
+
- webpack/__mocks__/foremanReact/components/common/MessageBox.js
|
175
|
+
- webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js
|
176
|
+
- webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js
|
177
|
+
- webpack/__mocks__/foremanReact/components/common/table.js
|
178
|
+
- webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js
|
179
|
+
- webpack/__mocks__/foremanReact/constants.js
|
180
|
+
- webpack/__mocks__/foremanReact/readme.md
|
181
|
+
- webpack/__mocks__/foremanReact/redux/actions/toasts.js
|
182
|
+
- webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js
|
183
|
+
- webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js
|
184
|
+
- webpack/__mocks__/foremanReact/routes/common/reducerHOC/withDataReducer.js
|
185
|
+
- webpack/fills_index.js
|
186
|
+
- webpack/index.js
|
187
|
+
- webpack/src/Components/StatisticsChartsList/StatisticsChartsList.fixtures.js
|
188
|
+
- webpack/src/Components/StatisticsChartsList/StatisticsChartsList.test.js
|
189
|
+
- webpack/src/Components/StatisticsChartsList/StatisticsChartsListStyles.scss
|
190
|
+
- webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap
|
191
|
+
- webpack/src/Components/StatisticsChartsList/index.js
|
192
|
+
- webpack/src/ForemanStatistics.js
|
193
|
+
- webpack/src/Router/StatisticsPage/Statistics/Statistics.js
|
194
|
+
- webpack/src/Router/StatisticsPage/StatisticsPage.fixtures.js
|
195
|
+
- webpack/src/Router/StatisticsPage/StatisticsPage.js
|
196
|
+
- webpack/src/Router/StatisticsPage/StatisticsPageActions.js
|
197
|
+
- webpack/src/Router/StatisticsPage/StatisticsPageSelectors.js
|
198
|
+
- webpack/src/Router/StatisticsPage/__tests__/StatisticsPage.test.js
|
199
|
+
- webpack/src/Router/StatisticsPage/__tests__/StatisticsPageActions.test.js
|
200
|
+
- webpack/src/Router/StatisticsPage/__tests__/StatisticsPageSelectors.test.js
|
201
|
+
- webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPage.test.js.snap
|
202
|
+
- webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap
|
203
|
+
- webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap
|
204
|
+
- webpack/src/Router/StatisticsPage/constants.js
|
205
|
+
- webpack/src/Router/StatisticsPage/index.js
|
206
|
+
- webpack/src/Router/__snapshots__/routes.test.js.snap
|
207
|
+
- webpack/src/Router/index.js
|
208
|
+
- webpack/src/Router/routes.js
|
209
|
+
- webpack/src/Router/routes.test.js
|
210
|
+
- webpack/src/index.js
|
211
|
+
- webpack/src/reducers.js
|
212
|
+
- webpack/src/trends.js
|
213
|
+
- webpack/src/trends.test.js
|
162
214
|
homepage: https://theforeman.org
|
163
215
|
licenses:
|
164
216
|
- GPL-3.0
|