katello 3.7.0 → 3.7.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/common/index.js +1 -0
  3. data/app/assets/javascripts/katello/sync_management/index.js +1 -0
  4. data/app/controllers/katello/api/v2/host_packages_controller.rb +1 -5
  5. data/app/controllers/katello/remote_execution_controller.rb +6 -6
  6. data/app/helpers/katello/hosts_and_hostgroups_helper.rb +37 -9
  7. data/app/lib/actions/katello/host/hypervisors_update.rb +82 -22
  8. data/app/lib/actions/pulp/consumer/abstract_content_action.rb +12 -0
  9. data/app/lib/actions/pulp/consumer/content_install.rb +1 -1
  10. data/app/lib/actions/pulp/consumer/content_uninstall.rb +1 -1
  11. data/app/lib/actions/pulp/consumer/content_update.rb +1 -1
  12. data/app/models/katello/concerns/subscription_facet_host_extensions.rb +1 -1
  13. data/app/models/katello/content_view.rb +12 -4
  14. data/app/models/katello/glue/candlepin/pool.rb +11 -11
  15. data/app/models/katello/host/content_facet.rb +2 -1
  16. data/app/models/katello/rpm.rb +14 -6
  17. data/app/models/katello/subscription_status.rb +1 -1
  18. data/app/services/katello/candlepin/consumer.rb +8 -0
  19. data/app/views/overrides/activation_keys/_host_environment_select.html.erb +2 -3
  20. data/config/routes.rb +1 -0
  21. data/db/migrate/20180612163403_add_foreign_key_to_hypervisor_id.rb +3 -0
  22. data/db/seeds.d/75-job_templates.rb +5 -2
  23. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-repository-sets-modal.controller.js +4 -3
  24. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-subscriptions-modal.controller.js +4 -1
  25. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/content-host-packages-installed.controller.js +1 -1
  26. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery-create.html +1 -1
  27. data/engines/bastion_katello/app/assets/stylesheets/bastion_katello/bastion_katello.scss +5 -0
  28. data/lib/katello/tasks/clean_backend_objects.rake +12 -3
  29. data/lib/katello/version.rb +1 -1
  30. data/package.json +10 -7
  31. data/webpack/__mocks__/foremanReact/redux.js +3 -0
  32. data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +2 -0
  33. data/webpack/components/Search/Search.test.js +3 -1
  34. data/webpack/components/SelectOrg/SelectOrg.scss +3 -0
  35. data/webpack/components/SelectOrg/SelectOrgAction.js +41 -0
  36. data/webpack/components/SelectOrg/SelectOrgReducer.js +33 -0
  37. data/webpack/components/SelectOrg/SetOrganization.js +116 -0
  38. data/webpack/components/WithOrganization/withOrganization.js +28 -0
  39. data/webpack/containers/Application/config.js +9 -2
  40. data/webpack/containers/Application/index.js +4 -2
  41. data/webpack/global_test_setup.js +6 -0
  42. data/webpack/helpers/caret.js +6 -0
  43. data/webpack/move_to_foreman/components/common/{emptyState → EmptyState}/index.js +16 -3
  44. data/webpack/move_to_foreman/components/common/table/components/Table.js +1 -1
  45. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/CollapseSubscriptionGroupButton.test.js.snap +2 -2
  46. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionCell.test.js.snap +1 -1
  47. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionHeaderCell.test.js.snap +1 -1
  48. data/webpack/move_to_pf/LoadingState/LoadingState.js +27 -14
  49. data/webpack/move_to_pf/LoadingState/LoadingState.test.js +8 -4
  50. data/webpack/move_to_pf/Select/Select.js +40 -0
  51. data/webpack/move_to_pf/react-bootstrap-select/index.js +12 -1
  52. data/webpack/redux/actions/RedHatRepositories/enabled.js +0 -1
  53. data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -5
  54. data/webpack/redux/consts.js +6 -0
  55. data/webpack/redux/reducers/index.js +2 -0
  56. data/webpack/scenes/Products/ProductActions.js +24 -0
  57. data/webpack/scenes/Products/ProductConstants.js +3 -0
  58. data/webpack/scenes/Products/__tests__/ProductActions.test.js +40 -0
  59. data/webpack/scenes/Products/__tests__/products.fixtures.js +90 -0
  60. data/webpack/scenes/RedHatRepositories/components/EnabledRepository.js +14 -23
  61. data/webpack/scenes/RedHatRepositories/components/EnabledRepositoryContent.js +34 -0
  62. data/webpack/scenes/RedHatRepositories/components/RepositorySetRepository.js +1 -1
  63. data/webpack/scenes/RedHatRepositories/components/SearchBar.js +1 -0
  64. data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepository.test.js +36 -0
  65. data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepositoryContent.test.js +27 -0
  66. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepository.test.js.snap +25 -0
  67. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepositoryContent.test.js.snap +47 -0
  68. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/RecommendedRepositorySetsToggler.test.js.snap +3 -1
  69. data/webpack/scenes/RedHatRepositories/index.js +7 -3
  70. data/webpack/scenes/RedHatRepositories/index.scss +1 -0
  71. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailActions.js +1 -1
  72. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailEnabledProducts.js +54 -0
  73. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProduct.js +29 -0
  74. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailReducer.js +29 -0
  75. data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.js +67 -22
  76. data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.scss +9 -0
  77. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailEnabledProducts.test.js +18 -0
  78. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailProduct.test.js +13 -0
  79. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetails.test.js +6 -0
  80. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailEnabledProducts.test.js.snap +45 -0
  81. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailProduct.test.js.snap +67 -0
  82. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +497 -410
  83. data/webpack/scenes/Subscriptions/Details/__tests__/subscriptionDetails.fixtures.js +4 -0
  84. data/webpack/scenes/Subscriptions/Details/index.js +3 -1
  85. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +78 -34
  86. data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryReducer.js +8 -0
  87. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +3 -0
  88. data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +34 -7
  89. data/webpack/scenes/Subscriptions/Manifest/index.js +1 -0
  90. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +1 -0
  91. data/webpack/scenes/Subscriptions/SubscriptionHelpers.js +3 -0
  92. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +6 -2
  93. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +31 -36
  94. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +2 -7
  95. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsPage.test.js +1 -1
  96. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +3 -6
  97. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +2 -0
  98. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +14 -2
  99. data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +4 -3
  100. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/EntitlementsInlineEditFormatter.js +8 -5
  101. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +29 -19
  102. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableHelpers.js +9 -2
  103. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +2 -2
  104. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/EntitlementsInlineEditFormatter.test.js +110 -0
  105. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +15 -3
  106. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/EntitlementsInlineEditFormatter.test.js.snap +228 -0
  107. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +54 -21
  108. data/webpack/scenes/Subscriptions/index.js +1 -0
  109. data/webpack/scenes/Tasks/helpers.js +52 -0
  110. data/webpack/services/api/index.js +17 -1
  111. data/webpack/test_setup.js +2 -0
  112. metadata +31 -4
  113. data/config/katello.yaml +0 -89
@@ -0,0 +1,28 @@
1
+ import React, { Component } from 'react';
2
+ import { orgId } from '../../services/api';
3
+ import SetOrganization from '../SelectOrg/SetOrganization';
4
+ import titleWithCaret from '../../helpers/caret';
5
+
6
+ function withOrganization(WrappedComponent, redirectPath) {
7
+ return class CheckOrg extends Component {
8
+ componentDidUpdate(prevProps) {
9
+ const { location } = this.props;
10
+
11
+ // TODO: use topbar react component
12
+ const orgTitle = location.state && location.state.orgChanged;
13
+ const prevOrgTitle = prevProps.location.state && prevProps.location.state.orgChanged;
14
+
15
+ if (orgTitle !== prevOrgTitle) {
16
+ document.getElementById('organization-dropdown').children[0].innerHTML = titleWithCaret(orgTitle);
17
+ }
18
+ }
19
+ render() {
20
+ if (!orgId()) {
21
+ return <SetOrganization redirectPath={redirectPath} />;
22
+ }
23
+ return <WrappedComponent {...this.props} />;
24
+ }
25
+ };
26
+ }
27
+
28
+ export default withOrganization;
@@ -2,25 +2,32 @@ import Repos from '../../scenes/RedHatRepositories';
2
2
  import Subscriptions from '../../scenes/Subscriptions';
3
3
  import UpstreamSubscriptions from '../../scenes/Subscriptions/UpstreamSubscriptions/index';
4
4
  import SubscriptionDetails from '../../scenes/Subscriptions/Details';
5
+ import SetOrganization from '../../components/SelectOrg/SetOrganization';
6
+ import WithOrganization from '../../components/WithOrganization/withOrganization';
5
7
 
6
8
  // eslint-disable-next-line import/prefer-default-export
7
9
  export const links = [
8
10
  {
9
11
  text: 'RH Repos',
10
12
  path: 'redhat_repositories',
11
- component: Repos,
13
+ component: WithOrganization(Repos, '/redhat_repositories'),
12
14
  },
13
15
  {
14
16
  text: 'RH Subscriptions',
15
17
  path: 'subscriptions',
16
- component: Subscriptions,
18
+ component: WithOrganization(Subscriptions, '/subscriptions'),
17
19
  },
18
20
  {
19
21
  path: 'subscriptions/add',
20
22
  component: UpstreamSubscriptions,
21
23
  },
22
24
  {
25
+ // eslint-disable-next-line no-useless-escape
23
26
  path: 'subscriptions/:id(\[0-9]*$\)',
24
27
  component: SubscriptionDetails,
25
28
  },
29
+ {
30
+ path: 'organization_select',
31
+ component: SetOrganization,
32
+ },
26
33
  ];
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import { BrowserRouter as Router } from 'react-router-dom';
4
4
  import { bindActionCreators } from 'redux';
5
5
  import { connect } from 'react-redux';
6
-
6
+ import { orgId } from '../../services/api';
7
7
  import * as actions from '../../scenes/Organizations/OrganizationActions';
8
8
  import reducer from '../../scenes/Organizations/OrganizationReducer';
9
9
  import Routes from './Routes';
@@ -20,7 +20,9 @@ class Application extends Component {
20
20
  }
21
21
 
22
22
  loadData() {
23
- this.props.loadOrganization();
23
+ if (orgId()) {
24
+ this.props.loadOrganization();
25
+ }
24
26
  }
25
27
 
26
28
  render() {
@@ -0,0 +1,6 @@
1
+ // runs before each test to make sure console.error output will
2
+ // fail a test (i.e. default PropType missing). Check the error
3
+ // output and traceback for actual error.
4
+ global.console.error = (error) => {
5
+ throw new Error(error);
6
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import ReactDOMServer from 'react-dom/server';
3
+
4
+ const htmlCaret = title => title + ReactDOMServer.renderToStaticMarkup(<span className="caret" />);
5
+
6
+ export default htmlCaret;
@@ -13,6 +13,7 @@ const EmptyState = (props) => {
13
13
  documentationButton = __('Documentation'),
14
14
  docUrl,
15
15
  action,
16
+ actionButton,
16
17
  secondayActions,
17
18
  } = props;
18
19
  const defaultDocumantion = `${documentationLabel} <a href=${docUrl}>${documentationButton}</a>`;
@@ -30,11 +31,23 @@ const EmptyState = (props) => {
30
31
  )}
31
32
  {action && (
32
33
  <PfEmptyState.Action>
33
- <LinkContainer to={action.url}>
34
- <Button href={action.url} bsStyle="primary" bsSize="large">
34
+ {action.url && (
35
+ <LinkContainer to={action.url}>
36
+ <Button href={action.url} bsStyle="primary" bsSize="large">
37
+ {action.title}
38
+ </Button>
39
+ </LinkContainer>
40
+ )}
41
+ {action.onClick && (
42
+ <Button onClick={action.onClick} bsStyle="primary" bsSize="large">
35
43
  {action.title}
36
44
  </Button>
37
- </LinkContainer>
45
+ )}
46
+ </PfEmptyState.Action>
47
+ )}
48
+ {actionButton && (
49
+ <PfEmptyState.Action>
50
+ {actionButton}
38
51
  </PfEmptyState.Action>
39
52
  )}
40
53
  {secondayActions && (
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Table as PfTable } from 'patternfly-react';
4
4
  import { noop } from 'foremanReact/common/helpers';
5
- import EmptyState from '../../emptyState';
5
+ import EmptyState from '../../EmptyState';
6
6
  import PaginationRow from '../../../../../components/PaginationRow/index';
7
7
 
8
8
  import TableBody from './TableBody';
@@ -4,7 +4,7 @@ exports[`CollapseSubscriptionGroupButton renders CollapseSubscriptionGroupButton
4
4
  <Icon
5
5
  className="collapse-subscription-group-button"
6
6
  name="angle-right"
7
- onClick={[Function]}
7
+ onClick={[MockFunction]}
8
8
  type="fa"
9
9
  />
10
10
  `;
@@ -13,7 +13,7 @@ exports[`CollapseSubscriptionGroupButton renders CollapseSubscriptionGroupButton
13
13
  <Icon
14
14
  className="collapse-subscription-group-button"
15
15
  name="angle-down"
16
- onClick={[Function]}
16
+ onClick={[MockFunction]}
17
17
  type="fa"
18
18
  />
19
19
  `;
@@ -9,7 +9,7 @@ exports[`TableSelectionCell renders TableSelectionCell 1`] = `
9
9
  checked={true}
10
10
  id="some id"
11
11
  label="some label"
12
- onChange={[Function]}
12
+ onChange={[MockFunction]}
13
13
  />
14
14
  some after
15
15
  </TableSelectionCell>
@@ -9,7 +9,7 @@ exports[`TableSelectionHeaderCell renders TableSelectionHeaderCell 1`] = `
9
9
  checked={true}
10
10
  id="some id"
11
11
  label="some label"
12
- onChange={[Function]}
12
+ onChange={[MockFunction]}
13
13
  />
14
14
  </TableSelectionHeading>
15
15
  `;
@@ -1,35 +1,48 @@
1
- import React from 'react';
1
+ import React, { Component } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Spinner } from 'patternfly-react';
4
4
  import './LoadingState.scss';
5
5
 
6
- const LoadingState = ({
7
- loading,
8
- loadingText,
9
- children,
10
- }) => {
11
- if (loading) {
12
- return (
6
+ class LoadingState extends Component {
7
+ constructor(props) {
8
+ super(props);
9
+ this.state = {
10
+ render: false,
11
+ };
12
+ }
13
+
14
+ componentDidMount() {
15
+ setTimeout(() => {
16
+ this.setState({ render: true });
17
+ }, this.props.timeout);
18
+ }
19
+
20
+ render() {
21
+ const { loading, loadingText, children } = this.props;
22
+ const spinner = (
13
23
  <div className="loading-state">
14
24
  <Spinner loading={loading} size="lg" />
15
25
  <p>{loadingText}</p>
16
- </div>
17
- );
18
- }
19
-
20
- return children;
21
- };
26
+ </div>);
22
27
 
28
+ if (loading) {
29
+ return this.state.render ? spinner : null;
30
+ }
31
+ return children;
32
+ }
33
+ }
23
34
  LoadingState.propTypes = {
24
35
  loading: PropTypes.bool,
25
36
  loadingText: PropTypes.string,
26
37
  children: PropTypes.node,
38
+ timeout: PropTypes.number,
27
39
  };
28
40
 
29
41
  LoadingState.defaultProps = {
30
42
  loading: false,
31
43
  loadingText: __('Loading'),
32
44
  children: null,
45
+ timeout: 300,
33
46
  };
34
47
 
35
48
  export default LoadingState;
@@ -6,6 +6,7 @@ import { shallow } from 'enzyme';
6
6
  import toJson from 'enzyme-to-json';
7
7
 
8
8
  import { LoadingState } from './index';
9
+ jest.useFakeTimers();
9
10
 
10
11
  test('Loading State renders properly while loading', () => {
11
12
  const component = shallow(
@@ -13,8 +14,10 @@ test('Loading State renders properly while loading', () => {
13
14
  <p>Loading Complete</p>
14
15
  </LoadingState>
15
16
  );
16
-
17
- expect(toJson(component.render())).toMatchSnapshot();
17
+ jest.runAllTimers();
18
+ component.update();
19
+ expect(component.state('render')).toEqual(true);
20
+ expect(toJson(component.render())).toMatchSnapshot();
18
21
  });
19
22
 
20
23
  test('Loading State renders properly while not loading', () => {
@@ -23,6 +26,7 @@ test('Loading State renders properly while not loading', () => {
23
26
  <p>Loading Complete</p>
24
27
  </LoadingState>
25
28
  );
26
-
27
- expect(toJson(component.render())).toMatchSnapshot();
29
+ jest.runAllTimers();
30
+ component.update();
31
+ expect(toJson(component.render())).toMatchSnapshot();
28
32
  });
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const Select = ({
5
+ placeholder, onChange, options, disabled, value,
6
+ }) => {
7
+ const renderOptions = arr =>
8
+ arr.map(({ name, id }) => (
9
+ <option key={id} value={id}>
10
+ {name}
11
+ </option>
12
+ ));
13
+
14
+ return (
15
+ <select
16
+ disabled={disabled}
17
+ className="form-control"
18
+ value={value}
19
+ onChange={onChange}
20
+ >
21
+ <option value="" disabled >{placeholder}</option>
22
+ {renderOptions(options)}
23
+ </select>
24
+ );
25
+ };
26
+
27
+ export default Select;
28
+
29
+ Select.propTypes = {
30
+ onChange: PropTypes.func.isRequired,
31
+ options: PropTypes.arrayOf(PropTypes.object).isRequired,
32
+ disabled: PropTypes.bool,
33
+ placeholder: PropTypes.string.isRequired,
34
+ value: PropTypes.string,
35
+ };
36
+
37
+ Select.defaultProps = {
38
+ disabled: false,
39
+ value: '',
40
+ };
@@ -3,6 +3,7 @@
3
3
  import React from 'react';
4
4
  import ReactDOM from 'react-dom';
5
5
  import { FormControl } from 'react-bootstrap';
6
+ import PropTypes from 'prop-types';
6
7
 
7
8
  require('jquery');
8
9
  require('bootstrap-select');
@@ -52,8 +53,10 @@ class BootstrapSelect extends React.Component {
52
53
  render() {
53
54
  // TODO: these classes are required because foreman assumes that all selects should use select2 and jquery multiselect
54
55
  // TODO: see also http://projects.theforeman.org/issues/21952
56
+ const { noneSelectedText } = this.props;
57
+
55
58
  return <FormControl {...this.props}
56
- data-none-selected-text={__('Nothing selected')}
59
+ data-none-selected-text={noneSelectedText}
57
60
  data-selected-text-format="count>3"
58
61
  data-count-selected-text={__('{0} items selected')}
59
62
  componentClass="select"
@@ -62,4 +65,12 @@ class BootstrapSelect extends React.Component {
62
65
  }
63
66
  }
64
67
 
68
+ BootstrapSelect.propTypes = {
69
+ noneSelectedText: PropTypes.string,
70
+ }
71
+
72
+ BootstrapSelect.defaultProps = {
73
+ noneSelectedText: __('Nothing selected'),
74
+ }
75
+
65
76
  export default BootstrapSelect;
@@ -34,7 +34,6 @@ export const createEnabledRepoParams = (extendedParams = {}) => {
34
34
  export const loadEnabledRepos = (extendedParams = {}) => (dispatch) => {
35
35
  dispatch({ type: ENABLED_REPOSITORIES_REQUEST, params: extendedParams });
36
36
  const { searchParams, repoParams } = createEnabledRepoParams(extendedParams);
37
-
38
37
  api
39
38
  .get('/repositories', {}, repoParams)
40
39
  .then(({ data }) => {
@@ -1,9 +1,9 @@
1
1
  const repoTypeSearchQueryMap = {
2
- rpm: '(name ~ rpms) and (name !~ source rpm) and (name !~ debug rpm)',
3
- sourceRpm: 'name ~ source rpm',
4
- debugRpm: 'name ~ debug rpm',
5
- kickstart: 'name ~ kickstart',
6
- ostree: 'name ~ ostree',
2
+ rpm: '(name !~ source rpm) and (name !~ debug rpm) and (content_type = yum)',
3
+ sourceRpm: '(name ~ source rpm) and (content_type = yum)',
4
+ debugRpm: '(name ~ debug rpm) and (content_type = yum)',
5
+ kickstart: 'content_type = kickstart',
6
+ ostree: 'content_type = ostree',
7
7
  beta: 'name ~ beta',
8
8
  };
9
9
 
@@ -17,3 +17,9 @@ export const REPOSITORY_SETS_UPDATE_RECOMMENDED = 'REPOSITORY_SETS_UPDATE_RECOMM
17
17
 
18
18
  export const REPOSITORY_ENABLED = 'REPOSITORY_ENABLED';
19
19
  export const REPOSITORY_DISABLED = 'REPOSITORY_DISABLED';
20
+
21
+ export const GET_ORGANIZATIONS_LIST_SUCCESS = 'GET_ORGANIZATIONS_LIST_SUCCESS';
22
+ export const GET_ORGANIZATIONS_LIST_FAILURE = 'GET_ORGANIZATIONS_LIST_FAILURE';
23
+ export const CHANGE_CURRENT_ORGANIZATION_SUCCESS = 'CHANGE_CURRENT_ORGANIZATION_SUCCESS';
24
+ export const CHANGE_CURRENT_ORGANIZATION_FAILURE = 'CHANGE_CURRENT_ORGANIZATION_FAILURE';
25
+ export const GET_ORGANIZATIONS_LIST_REQUEST = 'GET_ORGANIZATIONS_LIST_REQUEST';
@@ -5,6 +5,7 @@ import { subscriptions } from '../../scenes/Subscriptions';
5
5
  import { upstreamSubscriptions } from '../../scenes/Subscriptions/UpstreamSubscriptions';
6
6
  import { manifestHistory } from '../../scenes/Subscriptions/Manifest';
7
7
  import { subscriptionDetails } from '../../scenes/Subscriptions/Details';
8
+ import { setOrganization } from '../../components/SelectOrg/SetOrganization';
8
9
 
9
10
  export default combineReducers({
10
11
  organization,
@@ -13,4 +14,5 @@ export default combineReducers({
13
14
  upstreamSubscriptions,
14
15
  manifestHistory,
15
16
  subscriptionDetails,
17
+ setOrganization,
16
18
  });
@@ -0,0 +1,24 @@
1
+ import api, { orgId } from '../../services/api';
2
+
3
+ import {
4
+ PRODUCTS_REQUEST,
5
+ PRODUCTS_SUCCESS,
6
+ PRODUCTS_FAILURE,
7
+ } from './ProductConstants';
8
+ import { apiError } from '../../move_to_foreman/common/helpers.js';
9
+
10
+ export const loadProducts = (params = {}) => (dispatch) => {
11
+ dispatch({ type: PRODUCTS_REQUEST });
12
+
13
+ return api
14
+ .get(`/organizations/${orgId()}/products/`, {}, params)
15
+ .then(({ data }) => {
16
+ dispatch({
17
+ type: PRODUCTS_SUCCESS,
18
+ response: data,
19
+ });
20
+ })
21
+ .catch(result => dispatch(apiError(PRODUCTS_FAILURE, result)));
22
+ };
23
+
24
+ export default loadProducts;
@@ -0,0 +1,3 @@
1
+ export const PRODUCTS_REQUEST = 'PRODUCTS_REQUEST';
2
+ export const PRODUCTS_SUCCESS = 'PRODUCTS_SUCCESS';
3
+ export const PRODUCTS_FAILURE = 'PRODUCTS_FAILURE';
@@ -0,0 +1,40 @@
1
+ import thunk from 'redux-thunk';
2
+ import Immutable from 'seamless-immutable';
3
+ import configureMockStore from 'redux-mock-store';
4
+ import { mock, mockRequest, mockErrorRequest } from '../../../mockRequest';
5
+ import {
6
+ failureActions,
7
+ successActions,
8
+ requestSuccessResponse,
9
+ } from './products.fixtures';
10
+ import { loadProducts } from '../ProductActions';
11
+
12
+ const mockStore = configureMockStore([thunk]);
13
+ const store = mockStore({ e: Immutable({}) });
14
+
15
+ beforeEach(() => {
16
+ store.clearActions();
17
+ mock.reset();
18
+ });
19
+
20
+ describe('product actions', () => {
21
+ describe('loadProducts', () => {
22
+ it('handles failed PRODUCTS_REQUEST', () => {
23
+ mockErrorRequest({
24
+ url: '/katello/api/v2/organizations/1/products/',
25
+ status: 422,
26
+ });
27
+ return store.dispatch(loadProducts())
28
+ .then(() => expect(store.getActions()).toEqual(failureActions));
29
+ });
30
+
31
+ it('handles successful PRODUCTS_REQUEST', () => {
32
+ mockRequest({
33
+ url: '/katello/api/v2/organizations/1/products/',
34
+ response: requestSuccessResponse,
35
+ });
36
+ return store.dispatch(loadProducts())
37
+ .then(() => expect(store.getActions()).toEqual(successActions));
38
+ });
39
+ });
40
+ });