katello 4.1.4 → 4.2.0.rc1
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.
- checksums.yaml +4 -4
- data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +2 -2
- data/app/controllers/katello/api/v2/content_credentials_controller.rb +3 -3
- data/app/controllers/katello/api/v2/content_uploads_controller.rb +3 -1
- data/app/controllers/katello/api/v2/content_view_components_controller.rb +33 -1
- data/app/controllers/katello/api/v2/content_views_controller.rb +12 -0
- data/app/controllers/katello/api/v2/host_errata_controller.rb +1 -1
- data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -2
- data/app/controllers/katello/api/v2/products_controller.rb +4 -4
- data/app/controllers/katello/api/v2/repositories_bulk_actions_controller.rb +3 -11
- data/app/controllers/katello/api/v2/repositories_controller.rb +68 -47
- data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +0 -28
- data/app/controllers/katello/concerns/api/v2/registration_commands_controller_extensions.rb +26 -5
- data/app/controllers/katello/concerns/api/v2/registration_controller_extensions.rb +26 -1
- data/app/lib/actions/candlepin/environment/destroy.rb +2 -0
- data/app/lib/actions/katello/agent_action.rb +2 -2
- data/app/lib/actions/katello/capsule_content/sync_capsule.rb +3 -2
- data/app/lib/actions/katello/{gpg_key → content_credential}/update.rb +1 -1
- data/app/lib/actions/katello/content_view/publish.rb +6 -1
- data/app/lib/actions/katello/content_view_version/create_repos.rb +1 -1
- data/app/lib/actions/katello/content_view_version/incremental_update.rb +0 -47
- data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +1 -1
- data/app/lib/actions/katello/repository/clone_contents.rb +1 -7
- data/app/lib/actions/katello/repository/clone_to_environment.rb +1 -7
- data/app/lib/actions/katello/repository/create.rb +4 -8
- data/app/lib/actions/katello/repository/create_root.rb +1 -1
- data/app/lib/actions/katello/repository/destroy.rb +1 -3
- data/app/lib/actions/katello/repository/import_upload.rb +3 -2
- data/app/lib/actions/katello/repository/instance_update.rb +1 -1
- data/app/lib/actions/katello/repository/metadata_generate.rb +2 -8
- data/app/lib/actions/katello/repository/multi_clone_contents.rb +0 -1
- data/app/lib/actions/katello/repository/refresh_repository.rb +1 -4
- data/app/lib/actions/katello/repository/remove_content.rb +6 -4
- data/app/lib/actions/katello/repository/sync.rb +5 -25
- data/app/lib/actions/katello/repository/update.rb +1 -2
- data/app/lib/actions/katello/repository/update_http_proxy_details.rb +2 -5
- data/app/lib/actions/katello/repository/update_redhat_repository.rb +1 -1
- data/app/lib/actions/katello/repository/upload_files.rb +8 -3
- data/app/lib/actions/katello/repository/upload_package_group.rb +2 -11
- data/app/lib/actions/katello/repository/verify_checksum.rb +0 -1
- data/app/lib/actions/katello/repository_set/enable_repository.rb +1 -1
- data/app/lib/actions/pulp3/orchestration/repository/create.rb +2 -2
- data/app/lib/actions/pulp3/repository/create.rb +3 -4
- data/app/lib/actions/pulp3/repository/create_remote.rb +1 -6
- data/app/lib/actions/pulp3/repository/repair.rb +4 -0
- data/app/lib/katello/errors.rb +1 -0
- data/app/lib/katello/http_resource.rb +26 -73
- data/app/lib/katello/qpid/connection.rb +1 -3
- data/app/lib/katello/resources/candlepin/consumer.rb +1 -1
- data/app/lib/katello/resources/candlepin/environment.rb +2 -0
- data/app/lib/katello/resources/registry.rb +7 -20
- data/app/lib/katello/util/http_proxy.rb +0 -3
- data/app/lib/katello/validators/gpg_key_content_validator.rb +1 -1
- data/app/models/katello/authorization/{gpg_key.rb → content_credential.rb} +1 -1
- data/app/models/katello/authorization/product.rb +0 -4
- data/app/models/katello/concerns/host_managed_extensions.rb +2 -16
- data/app/models/katello/concerns/organization_extensions.rb +1 -1
- data/app/models/katello/concerns/pulp_database_unit.rb +13 -5
- data/app/models/katello/concerns/smart_proxy_extensions.rb +45 -41
- data/app/models/katello/{gpg_key.rb → content_credential.rb} +4 -4
- data/app/models/katello/content_view.rb +6 -1
- data/app/models/katello/generic_content_unit.rb +16 -0
- data/app/models/katello/glue/pulp/repos.rb +9 -25
- data/app/models/katello/kt_environment.rb +1 -1
- data/app/models/katello/product.rb +4 -4
- data/app/models/katello/repository.rb +13 -7
- data/app/models/katello/repository_generic_content_unit.rb +7 -0
- data/app/models/katello/root_repository.rb +38 -7
- data/app/models/setting/content.rb +5 -0
- data/app/services/cert/certs.rb +16 -8
- data/app/services/katello/applicability/applicable_content_helper.rb +1 -2
- data/app/services/katello/candlepin/consumer.rb +6 -0
- data/app/services/katello/component_view_presenter.rb +27 -0
- data/app/services/katello/pulp/repository.rb +1 -1
- data/app/services/katello/pulp/server.rb +2 -2
- data/app/services/katello/pulp3/api/core.rb +4 -0
- data/app/services/katello/pulp3/api/generic.rb +68 -0
- data/app/services/katello/pulp3/generic_content_unit.rb +29 -0
- data/app/services/katello/pulp3/pulp_content_unit.rb +5 -1
- data/app/services/katello/pulp3/repository/generic.rb +94 -0
- data/app/services/katello/pulp3/repository/yum.rb +4 -5
- data/app/services/katello/pulp3/repository.rb +27 -12
- data/app/services/katello/pulp3/repository_mirror.rb +2 -2
- data/app/services/katello/pulp3/smart_proxy_repository.rb +4 -4
- data/app/services/katello/registration_manager.rb +18 -7
- data/app/services/katello/repository_type.rb +59 -1
- data/app/services/katello/repository_type_manager.rb +116 -24
- data/app/views/katello/api/v2/content_views/base.json.rabl +4 -4
- data/app/views/katello/api/v2/repositories/show.json.rabl +1 -0
- data/app/views/smart_proxies/plugins/_pulpcore.html.erb +2 -5
- data/app/views/smart_proxies/pulp_status.html.erb +0 -7
- data/config/katello.yaml.example +0 -21
- data/config/routes/api/v2.rb +2 -1
- data/db/functions/deb_version_cmp_v01.sql +200 -0
- data/db/migrate/20171110082124_add_ssl_certs_to_products_and_repos.rb +5 -1
- data/db/migrate/20200402130013_add_repsoitory_docker_meta_tag_f_key.rb +3 -1
- data/db/migrate/20210624221630_katello_generic_content.rb +22 -0
- data/db/migrate/20210625095042_add_retain_package_versions_count.rb +9 -0
- data/db/migrate/20210628182553_add_generic_remote_options_to_root_repository.rb +5 -0
- data/db/migrate/20210714140440_remove_repo_export_permission.rb +5 -0
- data/db/migrate/20210721163730_change_gpg_keys_to_content_credentials.rb +8 -0
- data/db/migrate/20210728130748_create_function_deb_version_cmp.rb +12 -0
- data/db/seeds.d/111-upgrade_tasks.rb +1 -2
- data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +7 -5
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +4 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-details.html +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/apply-errata.controller.js +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/views/apply-errata-confirm.html +2 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +25 -33
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/product-repositories.controller.js +1 -6
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +10 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.filter.js +9 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details.controller.js +0 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-advanced-sync-options.html +1 -25
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-details.html +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +31 -13
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +11 -3
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/views/product-repositories.html +0 -6
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/aggregate-task.factory.js +3 -3
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/task.factory.js +1 -1
- data/lib/katello/engine.rb +2 -4
- data/lib/katello/permission_creator.rb +6 -12
- data/lib/katello/plugin.rb +76 -80
- data/lib/katello/repository_types/python.rb +37 -0
- data/lib/katello/tasks/reimport.rake +0 -9
- data/lib/katello/tasks/repository.rake +3 -4
- data/lib/katello/version.rb +1 -1
- data/locale/action_names.rb +28 -29
- data/locale/bn/katello.po +699 -221
- data/locale/cs/katello.po +167 -59
- data/locale/de/katello.po +585 -352
- data/locale/en/katello.po +167 -59
- data/locale/es/katello.po +1388 -1189
- data/locale/fr/katello.po +1740 -1494
- data/locale/gu/katello.po +896 -416
- data/locale/hi/katello.po +892 -415
- data/locale/it/katello.po +371 -170
- data/locale/ja/katello.po +1657 -1439
- data/locale/katello.pot +933 -736
- data/locale/kn/katello.po +894 -416
- data/locale/ko/katello.po +515 -317
- data/locale/mr/katello.po +857 -415
- data/locale/or/katello.po +894 -416
- data/locale/pa/katello.po +874 -411
- data/locale/pt/katello.po +347 -154
- data/locale/pt_BR/katello.po +1398 -1215
- data/locale/ru/katello.po +671 -463
- data/locale/ta/katello.po +697 -221
- data/locale/te/katello.po +891 -415
- data/locale/zh_CN/katello.po +2029 -1845
- data/locale/zh_TW/katello.po +735 -407
- data/package.json +3 -1
- data/webpack/components/EditableTextInput/EditableTextInput.js +3 -3
- data/webpack/components/RoutedTabs/RoutedTabs.js +7 -8
- data/webpack/components/Table/TableWrapper.js +19 -11
- data/webpack/components/Table/helpers.js +1 -1
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab.js +42 -0
- data/webpack/components/extensions/HostDetails/Tabs/SubscriptionTab.js +12 -0
- data/webpack/components/extensions/RegistrationCommands/__tests__/__snapshots__/ActivationKeys.test.js.snap +4 -0
- data/webpack/components/extensions/RegistrationCommands/fields/ActivationKeys.js +1 -1
- data/webpack/components/extensions/RegistrationCommands/index.js +1 -2
- data/webpack/components/pf3Table/formatters/selectionHeaderCellFormatter.js +2 -1
- data/webpack/fills_index.js +4 -1
- data/webpack/redux/actions/RedHatRepositories/helpers.js +2 -4
- data/webpack/redux/reducers/RedHatRepositories/enabled.js +4 -1
- data/webpack/scenes/ContentViews/ContentViewsActions.js +16 -1
- data/webpack/scenes/ContentViews/ContentViewsConstants.js +15 -0
- data/webpack/scenes/ContentViews/ContentViewsPage.js +12 -22
- data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +4 -3
- data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +25 -14
- data/webpack/scenes/ContentViews/Create/CreateContentViewModal.js +4 -2
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +153 -0
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentVersion.js +21 -10
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +157 -19
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.fixtures.json +100 -108
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +140 -16
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/publishedContentViewDetails.fixtures.json +367 -0
- data/webpack/scenes/ContentViews/Details/ContentViewDetailActions.js +59 -6
- data/webpack/scenes/ContentViews/Details/ContentViewDetailSelectors.js +43 -0
- data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +44 -13
- data/webpack/scenes/ContentViews/Details/Filters/Add/CVFilterAddModal.js +161 -0
- data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterAdd.test.js +54 -0
- data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterCreateResult.fixtures.json +124 -0
- data/webpack/scenes/ContentViews/Details/Filters/CVPackageGroupFilterContent.js +8 -6
- data/webpack/scenes/ContentViews/Details/Filters/CVRpmFilterContent.js +7 -6
- data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilterDetails.js +4 -3
- data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilters.js +71 -12
- data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilters.test.js +77 -0
- data/webpack/scenes/ContentViews/Details/Histories/ContentViewHistories.js +13 -12
- data/webpack/scenes/ContentViews/Details/Histories/__tests__/contentViewHistory.test.js +2 -2
- data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +17 -14
- data/webpack/scenes/ContentViews/Details/Repositories/LastSync.js +3 -3
- data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewAddRemove.test.js +2 -2
- data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewDetailRepos.test.js +6 -2
- data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +61 -20
- data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskInProgressResponse.fixtures.json +71 -0
- data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskResponse.fixtures.json +75 -0
- data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +86 -1
- data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersionsWithTask.fixtures.json +713 -0
- data/webpack/scenes/ContentViews/Details/__tests__/contentViewDetail.test.js +3 -0
- data/webpack/scenes/ContentViews/Publish/CVPublishFinish.js +184 -0
- data/webpack/scenes/ContentViews/Publish/CVPublishForm.js +104 -0
- data/webpack/scenes/ContentViews/Publish/CVPublishReview.js +71 -0
- data/webpack/scenes/ContentViews/Publish/ContentViewPublishSelectors.js +17 -0
- data/webpack/scenes/ContentViews/Publish/PublishContentViewWizard.js +145 -0
- data/webpack/scenes/ContentViews/Publish/__tests__/environmentPaths.fixtures.json +352 -0
- data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +184 -0
- data/webpack/scenes/ContentViews/Publish/__tests__/publishResponse.fixture.json +69 -0
- data/webpack/scenes/ContentViews/Publish/cvPublishForm.scss +3 -0
- data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +75 -48
- data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +15 -2
- data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +6 -10
- data/webpack/scenes/ContentViews/components/EnvironmentLabels.js +22 -10
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathActions.js +12 -0
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathConstants.js +2 -0
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathSelectors.js +16 -0
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +72 -0
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.scss +8 -0
- data/webpack/scenes/ContentViews/components/TaskPresenter/TaskPresenter.js +85 -0
- data/webpack/scenes/SmartProxy/SmartProxyContentTable.js +9 -8
- data/webpack/scenes/Subscriptions/SubscriptionsPage.js +4 -25
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +0 -3
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +3 -3
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +4 -2
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +24 -0
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Table.js +4 -1
- data/webpack/scenes/Subscriptions/index.js +1 -4
- metadata +74 -39
- data/app/lib/actions/candlepin/environment/create.rb +0 -21
- data/app/lib/actions/foreman/environment/destroy.rb +0 -23
- data/app/lib/actions/katello/content_view/environment_create.rb +0 -21
- data/app/lib/actions/katello/repository/export.rb +0 -85
- data/app/lib/actions/katello/repository/purge_empty_content.rb +0 -16
- data/app/lib/actions/katello/repository/upload_errata.rb +0 -38
- data/app/lib/katello/util/proxy_uri.rb +0 -64
- data/app/models/katello/rhsm_fact_importer.rb +0 -20
- data/app/models/katello/rhsm_fact_name.rb +0 -17
- data/app/models/katello/rhsm_fact_parser.rb +0 -120
@@ -1,5 +1,6 @@
|
|
1
|
-
import React, { useState,
|
2
|
-
import
|
1
|
+
import React, { useState, useCallback } from 'react';
|
2
|
+
import useDeepCompareEffect from 'use-deep-compare-effect';
|
3
|
+
import { useSelector } from 'react-redux';
|
3
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
4
5
|
import { STATUS } from 'foremanReact/constants';
|
5
6
|
import { Button } from '@patternfly/react-core';
|
@@ -9,30 +10,71 @@ import tableDataGenerator from './tableDataGenerator';
|
|
9
10
|
import getContentViews from '../ContentViewsActions';
|
10
11
|
import CreateContentViewModal from '../Create/CreateContentViewModal';
|
11
12
|
import CopyContentViewModal from '../Copy/CopyContentViewModal';
|
13
|
+
import PublishContentViewWizard from '../Publish/PublishContentViewWizard';
|
14
|
+
import { selectContentViews, selectContentViewStatus, selectContentViewError } from '../ContentViewSelectors';
|
12
15
|
|
13
|
-
const ContentViewTable = (
|
16
|
+
const ContentViewTable = () => {
|
17
|
+
const response = useSelector(selectContentViews);
|
18
|
+
const status = useSelector(selectContentViewStatus);
|
19
|
+
const error = useSelector(selectContentViewError);
|
14
20
|
const [table, setTable] = useState({ rows: [], columns: [] });
|
15
21
|
const [rowMappingIds, setRowMappingIds] = useState([]);
|
16
22
|
const [searchQuery, updateSearchQuery] = useState('');
|
17
|
-
const { results, ...metadata } = response;
|
18
23
|
const loadingResponse = status === STATUS.PENDING;
|
19
24
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
20
25
|
const [copy, setCopy] = useState(false);
|
26
|
+
const [cvResults, setCvResults] = useState([]);
|
27
|
+
const [metadata, setMetadata] = useState({});
|
28
|
+
const [cvTableStatus, setCvTableStatus] = useState(STATUS.PENDING);
|
29
|
+
const [isPublishModalOpen, setIsPublishModalOpen] = useState(false);
|
30
|
+
const [actionableCvDetails, setActionableCvDetails] = useState({});
|
21
31
|
const [actionableCvId, setActionableCvId] = useState('');
|
22
32
|
const [actionableCvName, setActionableCvName] = useState('');
|
23
|
-
|
24
|
-
setIsModalOpen(true);
|
25
|
-
}
|
33
|
+
const [currentStep, setCurrentStep] = useState(1);
|
26
34
|
|
27
|
-
|
35
|
+
const openForm = () => setIsModalOpen(true);
|
36
|
+
|
37
|
+
const openPublishModal = (rowInfo) => {
|
38
|
+
setActionableCvDetails({
|
39
|
+
id: rowInfo.cvId.toString(),
|
40
|
+
name: rowInfo.cvName,
|
41
|
+
composite: rowInfo.cvComposite,
|
42
|
+
version_count: rowInfo.cvVersionCount,
|
43
|
+
next_version: rowInfo.cvNextVersion,
|
44
|
+
});
|
45
|
+
setIsPublishModalOpen(true);
|
46
|
+
};
|
47
|
+
|
48
|
+
useDeepCompareEffect(
|
28
49
|
() => {
|
50
|
+
// Prevents flash of "No Content" before rows are loaded
|
51
|
+
const tableStatus = () => {
|
52
|
+
if (typeof cvResults === 'undefined' || status === STATUS.ERROR) return status; // will handle errored state
|
53
|
+
const resultsIds = Array.from(cvResults.map(result => result.id));
|
54
|
+
// All results are accounted for in row mapping, the page is ready to load
|
55
|
+
if (resultsIds.length === rowMappingIds.length &&
|
56
|
+
resultsIds.every(id => rowMappingIds.includes(id))) {
|
57
|
+
return status;
|
58
|
+
}
|
59
|
+
return STATUS.PENDING; // Fallback to pending
|
60
|
+
};
|
61
|
+
|
62
|
+
const { results, ...meta } = response;
|
63
|
+
if (status === STATUS.ERROR) {
|
64
|
+
setCvTableStatus(tableStatus());
|
65
|
+
}
|
29
66
|
if (!loadingResponse && results) {
|
67
|
+
setCvResults(results);
|
68
|
+
setMetadata(meta);
|
69
|
+
setCurrentStep(1);
|
30
70
|
const { newRowMappingIds, ...tableData } = tableDataGenerator(results);
|
31
71
|
setTable(tableData);
|
32
72
|
setRowMappingIds(newRowMappingIds);
|
73
|
+
setCvTableStatus(tableStatus());
|
33
74
|
}
|
34
75
|
},
|
35
|
-
[
|
76
|
+
[response, status, loadingResponse, setTable, setRowMappingIds,
|
77
|
+
setCvResults, setCvTableStatus, setCurrentStep, setMetadata, cvResults, rowMappingIds],
|
36
78
|
);
|
37
79
|
|
38
80
|
const onSelect = (_event, isSelected, rowId) => {
|
@@ -67,19 +109,18 @@ const ContentViewTable = ({ response, status, error }) => {
|
|
67
109
|
/* eslint-disable no-console */
|
68
110
|
return [
|
69
111
|
{
|
70
|
-
title: 'Publish
|
71
|
-
isDisabled: true,
|
112
|
+
title: __('Publish'),
|
72
113
|
onClick: (_event, rowId, rowInfo) => {
|
73
|
-
|
114
|
+
openPublishModal(rowInfo);
|
74
115
|
},
|
75
116
|
},
|
76
117
|
{
|
77
|
-
title: 'Promote',
|
118
|
+
title: __('Promote'),
|
78
119
|
isDisabled: true,
|
79
120
|
onClick: (_event, rowId, rowInfo) => console.log(`clicked on row ${rowInfo.cvName}`),
|
80
121
|
},
|
81
122
|
{
|
82
|
-
title: 'Copy',
|
123
|
+
title: __('Copy'),
|
83
124
|
onClick: (_event, rowId, rowInfo) => {
|
84
125
|
setCopy(true);
|
85
126
|
setActionableCvId(rowInfo.cvId.toString());
|
@@ -87,25 +128,15 @@ const ContentViewTable = ({ response, status, error }) => {
|
|
87
128
|
},
|
88
129
|
},
|
89
130
|
{
|
90
|
-
title: 'Delete',
|
131
|
+
title: __('Delete'),
|
91
132
|
isDisabled: true,
|
92
133
|
onClick: (_event, rowId, _rowInfo) => console.log(`clicked on row ${rowId}`),
|
93
134
|
},
|
94
135
|
];
|
95
136
|
/* eslint-enable no-console */
|
96
137
|
};
|
97
|
-
// Prevents flash of "No Content" before rows are loaded
|
98
|
-
const tableStatus = () => {
|
99
|
-
if (typeof results === 'undefined') return status; // will handle errored state
|
100
|
-
const resultsIds = Array.from(results.map(result => result.id));
|
101
|
-
// All results are accounted for in row mapping, the page is ready to load
|
102
|
-
if (resultsIds.length === rowMappingIds.length &&
|
103
|
-
resultsIds.every(id => rowMappingIds.includes(id))) {
|
104
|
-
return status;
|
105
|
-
}
|
106
|
-
return STATUS.PENDING; // Fallback to pending
|
107
|
-
};
|
108
138
|
|
139
|
+
const additionalListeners = new Array(isPublishModalOpen);
|
109
140
|
const emptyContentTitle = __("You currently don't have any Content Views.");
|
110
141
|
const emptyContentBody = __('A content view can be added by using the "New content view" button below.');
|
111
142
|
const emptySearchTitle = __('No matching content views found');
|
@@ -126,42 +157,38 @@ const ContentViewTable = ({ response, status, error }) => {
|
|
126
157
|
actionResolver,
|
127
158
|
searchQuery,
|
128
159
|
updateSearchQuery,
|
160
|
+
additionalListeners,
|
129
161
|
}}
|
130
162
|
variant={TableVariant.compact}
|
131
|
-
status={
|
132
|
-
fetchItems={getContentViews}
|
163
|
+
status={cvTableStatus}
|
164
|
+
fetchItems={useCallback(getContentViews, [])}
|
133
165
|
onCollapse={onCollapse}
|
134
166
|
canSelectAll={false}
|
135
167
|
cells={columns}
|
136
168
|
autocompleteEndpoint="/content_views/auto_complete_search"
|
137
169
|
>
|
138
|
-
|
170
|
+
<>
|
139
171
|
<Button onClick={openForm} variant="primary" aria-label="create_content_view">
|
140
172
|
Create content view
|
141
173
|
</Button>
|
142
174
|
<CreateContentViewModal show={isModalOpen} setIsOpen={setIsModalOpen} aria-label="create_content_view_modal" />
|
143
|
-
|
144
|
-
|
175
|
+
</>
|
176
|
+
<>
|
145
177
|
<CopyContentViewModal cvId={actionableCvId} cvName={actionableCvName} show={copy} setIsOpen={setCopy} aria-label="copy_content_view_modal" />
|
146
|
-
|
178
|
+
</>
|
179
|
+
{isPublishModalOpen &&
|
180
|
+
<>
|
181
|
+
<PublishContentViewWizard
|
182
|
+
details={actionableCvDetails}
|
183
|
+
show={isPublishModalOpen}
|
184
|
+
setIsOpen={setIsPublishModalOpen}
|
185
|
+
currentStep={currentStep}
|
186
|
+
setCurrentStep={setCurrentStep}
|
187
|
+
aria-label="publish_content_view_modal"
|
188
|
+
/>
|
189
|
+
</>}
|
147
190
|
</TableWrapper>
|
148
191
|
);
|
149
192
|
};
|
150
193
|
|
151
|
-
ContentViewTable.propTypes = {
|
152
|
-
response: PropTypes.shape({
|
153
|
-
results: PropTypes.arrayOf(PropTypes.shape({})),
|
154
|
-
}),
|
155
|
-
status: PropTypes.string.isRequired,
|
156
|
-
error: PropTypes.oneOfType([
|
157
|
-
PropTypes.shape({}),
|
158
|
-
PropTypes.string,
|
159
|
-
]),
|
160
|
-
};
|
161
|
-
|
162
|
-
ContentViewTable.defaultProps = {
|
163
|
-
error: null,
|
164
|
-
response: { results: [] },
|
165
|
-
};
|
166
|
-
|
167
194
|
export default ContentViewTable;
|
@@ -44,11 +44,24 @@ const buildExpandableRows = (contentViews) => {
|
|
44
44
|
|
45
45
|
contentViews.forEach((contentView) => {
|
46
46
|
const {
|
47
|
-
id,
|
47
|
+
id,
|
48
|
+
name,
|
49
|
+
composite,
|
50
|
+
next_version: nextVersion,
|
51
|
+
version_count: versionCount,
|
52
|
+
description,
|
53
|
+
activation_keys: activationKeys,
|
54
|
+
hosts,
|
48
55
|
} = contentView;
|
49
56
|
const cells = buildRow(contentView);
|
50
57
|
const cellParent = {
|
51
|
-
cvId: id,
|
58
|
+
cvId: id,
|
59
|
+
cvName: name,
|
60
|
+
cvVersionCount: versionCount,
|
61
|
+
cvComposite: composite,
|
62
|
+
cvNextVersion: nextVersion,
|
63
|
+
isOpen: false,
|
64
|
+
cells,
|
52
65
|
};
|
53
66
|
rows.push(cellParent);
|
54
67
|
const cellChild = {
|
@@ -50,7 +50,7 @@ test('Can call API for CVs and show on screen on page load', async (done) => {
|
|
50
50
|
// Assert that the CV is not showing yet by searching by name and the query returning null
|
51
51
|
expect(queryByText(firstCV.name)).toBeNull();
|
52
52
|
// Assert that the CV name is now showing on the screen, but wait for it to appear.
|
53
|
-
await patientlyWaitFor(() => expect(queryByText(firstCV.name)).
|
53
|
+
await patientlyWaitFor(() => expect(queryByText(firstCV.name)).toBeInTheDocument());
|
54
54
|
// Assert request was made and completed, see helper function
|
55
55
|
assertNockRequest(autocompleteScope);
|
56
56
|
assertNockRequest(scope, done); // Pass jest callback to confirm test is done
|
@@ -168,7 +168,7 @@ test('Can handle no Content Views being present', async (done) => {
|
|
168
168
|
const { queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions);
|
169
169
|
|
170
170
|
expect(queryByText(firstCV.name)).toBeNull();
|
171
|
-
await patientlyWaitFor(() => expect(queryByText(/don't have any Content Views/i)).
|
171
|
+
await patientlyWaitFor(() => expect(queryByText(/don't have any Content Views/i)).toBeInTheDocument());
|
172
172
|
assertNockRequest(autocompleteScope);
|
173
173
|
assertNockRequest(scope, done);
|
174
174
|
});
|
@@ -183,7 +183,7 @@ test('Can handle errored response', async (done) => {
|
|
183
183
|
const { queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions);
|
184
184
|
|
185
185
|
expect(queryByText(firstCV.name)).toBeNull();
|
186
|
-
await patientlyWaitFor(() => expect(queryByText(/unable to connect/i)).
|
186
|
+
await patientlyWaitFor(() => expect(queryByText(/unable to connect/i)).toBeInTheDocument());
|
187
187
|
assertNockRequest(autocompleteScope);
|
188
188
|
assertNockRequest(scope, done);
|
189
189
|
});
|
@@ -231,18 +231,16 @@ test('Can handle pagination', async (done) => {
|
|
231
231
|
const firstPageScope = nockInstance
|
232
232
|
.get(cvIndexPath)
|
233
233
|
// Using a custom query params matcher because parameters can be strings
|
234
|
-
.query(actualQueryObject => parseInt(actualQueryObject.page, 10) === 1)
|
234
|
+
.query(actualQueryObject => (parseInt(actualQueryObject.page, 10) === 1))
|
235
235
|
.reply(200, cvIndexFirstPage);
|
236
236
|
|
237
237
|
// Match second page API request
|
238
238
|
const secondPageScope = nockInstance
|
239
239
|
.get(cvIndexPath)
|
240
240
|
// Using a custom query params matcher because parameters can be strings
|
241
|
-
.query(actualQueryObject => parseInt(actualQueryObject.page, 10) === 2)
|
241
|
+
.query(actualQueryObject => (parseInt(actualQueryObject.page, 10) === 2))
|
242
242
|
.reply(200, cvIndexSecondPage);
|
243
|
-
|
244
243
|
const { queryByText, getByLabelText } = renderWithRedux(<ContentViewsPage />, renderOptions);
|
245
|
-
|
246
244
|
// Wait for first paginated page to load and assert only the first page of results are present
|
247
245
|
await patientlyWaitFor(() => {
|
248
246
|
expect(queryByText(results[0].name)).toBeInTheDocument();
|
@@ -253,14 +251,12 @@ test('Can handle pagination', async (done) => {
|
|
253
251
|
// Label comes from patternfly, if this test fails, check if patternfly updated the label.
|
254
252
|
expect(getByLabelText('Go to next page')).toBeTruthy();
|
255
253
|
getByLabelText('Go to next page').click();
|
256
|
-
|
257
254
|
// Wait for second paginated page to load and assert only the second page of results are present
|
258
255
|
await patientlyWaitFor(() => {
|
259
256
|
expect(queryByText(results[20].name)).toBeInTheDocument();
|
260
257
|
expect(queryByText(results[39].name)).toBeInTheDocument();
|
261
258
|
expect(queryByText(results[41].name)).not.toBeInTheDocument();
|
262
259
|
});
|
263
|
-
|
264
260
|
assertNockRequest(autocompleteScope);
|
265
261
|
assertNockRequest(firstPageScope);
|
266
262
|
assertNockRequest(secondPageScope, done); // Only pass jest callback to the last API request
|
@@ -359,7 +355,7 @@ test('Displays Create Content View and opens modal with Form', async () => {
|
|
359
355
|
const {
|
360
356
|
getByText, queryByText, getByLabelText,
|
361
357
|
} = renderWithRedux(<ContentViewsPage />, renderOptions);
|
362
|
-
await patientlyWaitFor(() => expect(queryByText('Create content view')).
|
358
|
+
await patientlyWaitFor(() => expect(queryByText('Create content view')).toBeInTheDocument());
|
363
359
|
|
364
360
|
expect(queryByText('Description')).not.toBeInTheDocument();
|
365
361
|
expect(queryByText('Name')).not.toBeInTheDocument();
|
@@ -1,16 +1,28 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import PropTypes from 'prop-types';
|
3
2
|
import { Label } from '@patternfly/react-core';
|
4
3
|
|
5
|
-
const EnvironmentLabels = environments =>
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
const EnvironmentLabels = (environments) => {
|
5
|
+
const { environments: singleEnvironment } = environments || {};
|
6
|
+
const { name } = singleEnvironment || {};
|
7
|
+
switch (environments) {
|
8
|
+
case Array:
|
9
|
+
return environments.map(env => (
|
10
|
+
<React.Fragment key={env.id} style={{ marginBottom: '5px' }}>
|
11
|
+
<Label
|
12
|
+
color="purple"
|
13
|
+
>{`${env.name}`}
|
14
|
+
</Label>
|
15
|
+
</React.Fragment>
|
16
|
+
));
|
17
|
+
default:
|
18
|
+
return (
|
19
|
+
<React.Fragment>
|
20
|
+
<Label color="purple">
|
21
|
+
{`${name}`}
|
22
|
+
</Label>
|
23
|
+
</React.Fragment>
|
24
|
+
);
|
25
|
+
}
|
14
26
|
};
|
15
27
|
|
16
28
|
export default EnvironmentLabels;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { API_OPERATIONS, get } from 'foremanReact/redux/API';
|
2
|
+
import api, { orgId } from '../../../../services/api';
|
3
|
+
import { ENVIRONMENT_PATHS_KEY } from './EnvironmentPathConstants';
|
4
|
+
|
5
|
+
|
6
|
+
const getEnvironmentPaths = () => get({
|
7
|
+
type: API_OPERATIONS.GET,
|
8
|
+
key: ENVIRONMENT_PATHS_KEY,
|
9
|
+
url: api.getApiUrl(`/organizations/${orgId()}/environments/paths?permission_type=promotable`),
|
10
|
+
});
|
11
|
+
|
12
|
+
export default getEnvironmentPaths;
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import {
|
2
|
+
selectAPIStatus,
|
3
|
+
selectAPIError,
|
4
|
+
selectAPIResponse,
|
5
|
+
} from 'foremanReact/redux/API/APISelectors';
|
6
|
+
import { STATUS } from 'foremanReact/constants';
|
7
|
+
import { ENVIRONMENT_PATHS_KEY } from './EnvironmentPathConstants';
|
8
|
+
|
9
|
+
export const selectEnvironmentPaths = state =>
|
10
|
+
selectAPIResponse(state, ENVIRONMENT_PATHS_KEY) || {};
|
11
|
+
|
12
|
+
export const selectEnvironmentPathsStatus = state =>
|
13
|
+
selectAPIStatus(state, ENVIRONMENT_PATHS_KEY) || STATUS.PENDING;
|
14
|
+
|
15
|
+
export const selectEnvironmentPathsError = state =>
|
16
|
+
selectAPIError(state, ENVIRONMENT_PATHS_KEY);
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useSelector } from 'react-redux';
|
3
|
+
import { STATUS } from 'foremanReact/constants';
|
4
|
+
import PropTypes from 'prop-types';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import { Form, FormGroup, Checkbox } from '@patternfly/react-core';
|
7
|
+
import { selectEnvironmentPaths, selectEnvironmentPathsStatus } from './EnvironmentPathSelectors';
|
8
|
+
import EnvironmentLabels from '../EnvironmentLabels';
|
9
|
+
import './EnvironmentPaths.scss';
|
10
|
+
import Loading from '../../../../components/Loading';
|
11
|
+
|
12
|
+
const EnvironmentPaths = ({ userCheckedItems, setUserCheckedItems }) => {
|
13
|
+
const environmentPathResponse = useSelector(selectEnvironmentPaths);
|
14
|
+
const environmentPathStatus = useSelector(selectEnvironmentPathsStatus);
|
15
|
+
const environmentPathLoading = environmentPathStatus === STATUS.PENDING;
|
16
|
+
|
17
|
+
const oncheckedChange = (checked, env) => {
|
18
|
+
if (checked) {
|
19
|
+
setUserCheckedItems([...userCheckedItems, env]);
|
20
|
+
} else {
|
21
|
+
setUserCheckedItems(userCheckedItems.filter(item => item.id !== env.id));
|
22
|
+
}
|
23
|
+
};
|
24
|
+
if (environmentPathLoading) {
|
25
|
+
return <Loading />;
|
26
|
+
}
|
27
|
+
const { results } = environmentPathResponse || {};
|
28
|
+
/* eslint-disable react/no-array-index-key */
|
29
|
+
return (
|
30
|
+
<>
|
31
|
+
<p
|
32
|
+
style={{ marginBottom: '2px' }}
|
33
|
+
>{__('Select a lifecycle environment from the available promotion paths to promote new version.')}
|
34
|
+
</p>
|
35
|
+
<Form>{results.map((path, count) => {
|
36
|
+
const {
|
37
|
+
environments,
|
38
|
+
} = path || {};
|
39
|
+
return (
|
40
|
+
<React.Fragment key={count}>
|
41
|
+
<FormGroup key={`fg-${count}`} isInline fieldId="environment-checkbox-group">
|
42
|
+
{environments.map(env =>
|
43
|
+
(<Checkbox
|
44
|
+
isChecked={env.library ||
|
45
|
+
userCheckedItems.filter(item => item.id === env.id).length}
|
46
|
+
isDisabled={env.library}
|
47
|
+
style={{ marginRight: '3px', marginBottom: '1px' }}
|
48
|
+
className="env-labels-with-pointer"
|
49
|
+
key={`${env.id}${count}`}
|
50
|
+
id={`${env.id}${count}`}
|
51
|
+
label={<EnvironmentLabels environments={env} />}
|
52
|
+
aria-label={env.label}
|
53
|
+
onChange={checked => oncheckedChange(checked, env)}
|
54
|
+
/>))}
|
55
|
+
</FormGroup>
|
56
|
+
<hr key={`hr${count}`} style={{ margin: '0em' }} />
|
57
|
+
</React.Fragment>
|
58
|
+
);
|
59
|
+
})}
|
60
|
+
</Form>
|
61
|
+
</>
|
62
|
+
);
|
63
|
+
/* eslint-enable react/no-array-index-key */
|
64
|
+
};
|
65
|
+
|
66
|
+
EnvironmentPaths.propTypes = {
|
67
|
+
userCheckedItems: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
68
|
+
setUserCheckedItems: PropTypes.func.isRequired,
|
69
|
+
};
|
70
|
+
|
71
|
+
|
72
|
+
export default EnvironmentPaths;
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
3
|
+
import useDeepCompareEffect from 'use-deep-compare-effect';
|
4
|
+
import { InProgressIcon } from '@patternfly/react-icons';
|
5
|
+
import PropTypes from 'prop-types';
|
6
|
+
import { Progress,
|
7
|
+
ProgressSize,
|
8
|
+
ProgressMeasureLocation,
|
9
|
+
ProgressVariant } from '@patternfly/react-core';
|
10
|
+
import { STATUS } from 'foremanReact/constants';
|
11
|
+
import { stopPollingTask, toastTaskFinished } from '../../../Tasks/TaskActions';
|
12
|
+
import { selectTaskPoll, selectTaskPollStatus } from '../../Details/ContentViewDetailSelectors';
|
13
|
+
|
14
|
+
const TaskPresenter = ({ activeHistory, setPollingFinished }) => {
|
15
|
+
const { task } = activeHistory;
|
16
|
+
const dispatch = useDispatch();
|
17
|
+
const [polling, setPolling] = useState(true);
|
18
|
+
const [taskErrored, setTaskErrored] = useState(task.result === 'error');
|
19
|
+
const pollResponse = useSelector(state =>
|
20
|
+
selectTaskPoll(state, task.id));
|
21
|
+
const pollResponseStatus = useSelector(state =>
|
22
|
+
selectTaskPollStatus(state, task.id));
|
23
|
+
const loading = pollResponseStatus === STATUS.PENDING;
|
24
|
+
|
25
|
+
const progressCompleted = () => (
|
26
|
+
pollResponse.progress ?
|
27
|
+
pollResponse.progress * 100 :
|
28
|
+
task.progress * 100
|
29
|
+
);
|
30
|
+
|
31
|
+
useEffect(() => {
|
32
|
+
if (!polling) {
|
33
|
+
const { id } = task;
|
34
|
+
dispatch(stopPollingTask(id));
|
35
|
+
dispatch(toastTaskFinished(pollResponse));
|
36
|
+
setPollingFinished(true); // Use this boolean as activeListener in refering page table
|
37
|
+
}
|
38
|
+
}, [polling, dispatch, setPollingFinished, pollResponse, task]);
|
39
|
+
|
40
|
+
useDeepCompareEffect(() => {
|
41
|
+
if (!loading && polling) {
|
42
|
+
const { state, result } = pollResponse;
|
43
|
+
if ((state === 'paused' || result === 'error') && !taskErrored) {
|
44
|
+
setTaskErrored(true);
|
45
|
+
setPolling(false);
|
46
|
+
} else if (state === 'stopped' && result === 'success') {
|
47
|
+
setPolling(false);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}, [pollResponse, loading, taskErrored, setTaskErrored, polling, setPolling]);
|
51
|
+
|
52
|
+
if (pollResponse) {
|
53
|
+
return (
|
54
|
+
<a href={`/foreman_tasks/tasks/${task.id}`} target="_blank" rel="noreferrer">
|
55
|
+
<Progress
|
56
|
+
aria-label="task_presenter"
|
57
|
+
value={progressCompleted()}
|
58
|
+
measureLocation={ProgressMeasureLocation.inside}
|
59
|
+
variant={taskErrored ? ProgressVariant.danger : ProgressVariant.default}
|
60
|
+
size={ProgressSize.sm}
|
61
|
+
/>
|
62
|
+
</a>
|
63
|
+
);
|
64
|
+
}
|
65
|
+
return (
|
66
|
+
<InProgressIcon />
|
67
|
+
);
|
68
|
+
};
|
69
|
+
|
70
|
+
TaskPresenter.propTypes = {
|
71
|
+
activeHistory: PropTypes.shape({
|
72
|
+
task: PropTypes.shape({
|
73
|
+
id: PropTypes.oneOfType([
|
74
|
+
PropTypes.number,
|
75
|
+
PropTypes.string,
|
76
|
+
]).isRequired,
|
77
|
+
result: PropTypes.string.isRequired,
|
78
|
+
progress: PropTypes.number.isRequired,
|
79
|
+
}).isRequired,
|
80
|
+
}).isRequired,
|
81
|
+
setPollingFinished: PropTypes.func.isRequired,
|
82
|
+
};
|
83
|
+
|
84
|
+
|
85
|
+
export default TaskPresenter;
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
2
|
+
import useDeepCompareEffect from 'use-deep-compare-effect';
|
2
3
|
import PropTypes from 'prop-types';
|
3
4
|
import { useSelector, useDispatch } from 'react-redux';
|
4
5
|
import { wrappable } from '@patternfly/react-table';
|
@@ -49,10 +50,6 @@ const SmartProxyContentTable = ({ smartProxyId }) => {
|
|
49
50
|
},
|
50
51
|
];
|
51
52
|
|
52
|
-
const fetchWithParams = () => {
|
53
|
-
dispatch(getSmartProxyContent({ smartProxyId }));
|
54
|
-
};
|
55
|
-
|
56
53
|
const buildrows = (results) => {
|
57
54
|
const newRows = [];
|
58
55
|
let envCount = 0;
|
@@ -125,15 +122,19 @@ const SmartProxyContentTable = ({ smartProxyId }) => {
|
|
125
122
|
setRow(newRows);
|
126
123
|
};
|
127
124
|
|
125
|
+
useEffect(
|
126
|
+
() => {
|
127
|
+
dispatch(getSmartProxyContent({ smartProxyId }));
|
128
|
+
}
|
129
|
+
, [dispatch, smartProxyId],
|
130
|
+
);
|
128
131
|
|
129
|
-
|
130
|
-
|
131
|
-
useEffect(() => {
|
132
|
+
useDeepCompareEffect(() => {
|
132
133
|
if (status !== STATUS.PENDING && response) {
|
133
134
|
const { lifecycle_environments: env } = response;
|
134
135
|
setRows(buildrows(env));
|
135
136
|
}
|
136
|
-
}, [
|
137
|
+
}, [response, status, error]);
|
137
138
|
|
138
139
|
|
139
140
|
return (
|