diff --git a/.travis.yml b/.travis.yml index df3f33518..087970eb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,22 @@ language: node_js +sudo: required notifications: email: false node_js: - - "16" +- '16' install: - npm ci jobs: include: - - stage: Lint - script: npm run build && npm run lint - - stage: Test - script: npm run test && npm run test:ct - after_success: npm run coverage - - stage: Deploy - if: (fork = false) AND (branch IN (master, master-stable, prod-beta, prod-stable)) - script: npm run build && curl -sSL https://raw.githubusercontent.com/RedHatInsights/insights-frontend-builder-common/master/src/bootstrap.sh | bash -s - - stage: Tag - if: (fork = false) AND (branch = master) - script: npx semantic-release + - stage: Lint, tests + script: npm run verify && npm run test:ct && npm run coverage + - stage: Release tag + if: fork = false + script: npx semantic-release +after_success: +- curl -sSL https://raw.githubusercontent.com/RedHatInsights/insights-frontend-builder-common/master/src/bootstrap.sh | bash -s env: - global: + global: - REPO="git@github.com:RedHatInsights/insights-inventory-frontend-build" - REPO_DIR="insights-inventory-frontend-build" - - BRANCH=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} + - BRANCH=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 99eb5fb9c..63f843372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,152 +1,3 @@ -## [1.14.8](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.7...v1.14.8) (2023-04-14) - - -### Bug Fixes - -* **LastSeenFilter:** Add edge cases for new filter ([f53155a](https://github.com/RedHatInsights/insights-inventory-frontend/commit/f53155a89bb874c9c5d6665a9f6dce23b029ecb9)) - - -### Reverts - -* Revert "ESSNTL(4056): Adds checks against edge cases for last seen filter (#1787)" ([1f0b70c](https://github.com/RedHatInsights/insights-inventory-frontend/commit/1f0b70c15749126a7d8a605c8e890fa6d0881df6)), closes [#1787](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1787) - -## [1.14.8](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.7...v1.14.8) (2023-04-13) - - -### Reverts - -* Revert "ESSNTL(4056): Adds checks against edge cases for last seen filter (#1787)" ([1f0b70c](https://github.com/RedHatInsights/insights-inventory-frontend/commit/1f0b70c15749126a7d8a605c8e890fa6d0881df6)), closes [#1787](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1787) - -## [1.14.7](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.6...v1.14.7) (2023-04-11) - - -### Bug Fixes - -* **inventory groups:** changed the column order to allign with the app ([#1828](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1828)) ([3124e53](https://github.com/RedHatInsights/insights-inventory-frontend/commit/3124e53dd63208a5c8a7a29e1e604b4850f6f204)) - -## [1.14.6](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.5...v1.14.6) (2023-04-06) - - -### Bug Fixes - -* **ESSNTL-3729:** Fix add button behavior ([#1825](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1825)) ([cdf9848](https://github.com/RedHatInsights/insights-inventory-frontend/commit/cdf9848dd9059052133cab8bf649c53ce2829109)) - -## [1.14.5](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.4...v1.14.5) (2023-04-03) - - -### Bug Fixes - -* **RHIF-232:** flickering inventory table is fixed, api reqs are reduced to 1 ([97af8a3](https://github.com/RedHatInsights/insights-inventory-frontend/commit/97af8a327d04e4aa4c676f5376502312bf44cbeb)) - -## [1.14.4](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.3...v1.14.4) (2023-04-03) - - -### Bug Fixes - -* **ESSNTL-3728:** Fix links, show missing tags filter ([#1824](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1824)) ([ebfad9c](https://github.com/RedHatInsights/insights-inventory-frontend/commit/ebfad9cdafcdb2d68176de9d5290a3db52579d2f)) - -## [1.14.3](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.2...v1.14.3) (2023-04-03) - - -### Bug Fixes - -* **ESSNTL-3727:** Hide Group filter ([#1823](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1823)) ([7657a7e](https://github.com/RedHatInsights/insights-inventory-frontend/commit/7657a7e33e74008a5121ea3253c823d92b761290)) - -## [1.14.2](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.1...v1.14.2) (2023-03-31) - - -### Bug Fixes - -* **ESSNTL-3727:** Fix minor issues for groups/%id view ([#1820](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1820)) ([1fa8d01](https://github.com/RedHatInsights/insights-inventory-frontend/commit/1fa8d013cf45c291891b58f9158eed6e95de7dd2)) - -## [1.14.1](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.14.0...v1.14.1) (2023-03-30) - - -### Bug Fixes - -* **ESSNTL-4196:** Tie URLs to chrome isBeta ([#1817](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1817)) ([7c686e5](https://github.com/RedHatInsights/insights-inventory-frontend/commit/7c686e5a2d104197ac05122d3cf532b899ed66a2)) - -# [1.14.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.13.0...v1.14.0) (2023-03-30) - - -### Features - -* **ESSNTL-3728:** Enable multiple hosts addition to group ([#1798](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1798)) ([c9a1d4e](https://github.com/RedHatInsights/insights-inventory-frontend/commit/c9a1d4e1899df65004878eec1574613e355dab7a)) - -# [1.13.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.12.3...v1.13.0) (2023-03-29) - - -### Features - -* **ESSNTL-3729:** Add new actions to kebab and new modal ([#1794](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1794)) ([011f64c](https://github.com/RedHatInsights/insights-inventory-frontend/commit/011f64c998573f9b8e9012c0c8c70f63e2d08532)) - -## [1.12.3](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.12.2...v1.12.3) (2023-03-28) - - -### Bug Fixes - -* **RHCLOUD-24793:** Show ROS tab is azure or aws cloud provider ([#1800](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1800)) ([2331f03](https://github.com/RedHatInsights/insights-inventory-frontend/commit/2331f0364a73c1916c19b864c04a4e512a10cd6a)) - -## [1.12.2](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.12.1...v1.12.2) (2023-03-22) - -## [1.12.1](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.12.0...v1.12.1) (2023-03-22) - - -### Bug Fixes - -* Fix the CI error related to lastSeen filter ([#1802](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1802)) ([a19f6aa](https://github.com/RedHatInsights/insights-inventory-frontend/commit/a19f6aa92e7fa31d8b4bf5356784f2922f213ddc)) - -# [1.12.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.11.0...v1.12.0) (2023-03-15) - - -### Features - -* **ESSNTL-4195:** Inventory table - group filter ([#1776](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1776)) ([358400e](https://github.com/RedHatInsights/insights-inventory-frontend/commit/358400ec23e1f64933cd2dfea3cd92fc568459b8)) - -# [1.11.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.10.1...v1.11.0) (2023-03-15) - - -### Features - -* **ESSNTL-4196:** Show group detail info tab ([#1792](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1792)) ([f0f421b](https://github.com/RedHatInsights/insights-inventory-frontend/commit/f0f421b0b5b2f577c3fd594f663c699ffcd44358)) - -## [1.10.1](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.10.0...v1.10.1) (2023-03-14) - - -### Bug Fixes - -* **ESSNTL-3760:** handle insights disconnected hosts for patch, advisor, vuln tabs ([#1791](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1791)) ([f8d7afd](https://github.com/RedHatInsights/insights-inventory-frontend/commit/f8d7afd9fde1f1a85aa1ff2033a6cdceb3019235)) - -# [1.10.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.9.2...v1.10.0) (2023-03-14) - - -### Features - -* **ESSNTL-3727:** Display group systems ([#1790](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1790)) ([08408ad](https://github.com/RedHatInsights/insights-inventory-frontend/commit/08408addd3fb27a333e2c376e9290b9049757860)) - -## [1.9.2](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.9.1...v1.9.2) (2023-03-13) - - -### Bug Fixes - -* **ESSNTL-4404:** global filters and tags ([#1788](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1788)) ([b10ce31](https://github.com/RedHatInsights/insights-inventory-frontend/commit/b10ce310766b6b463d82b5ed675328b411a28a14)) - -## [1.9.1](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.9.0...v1.9.1) (2023-03-13) - -# [1.9.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.8.0...v1.9.0) (2023-03-13) - - -### Features - -* **ESSNTL-3737, -3735:** Rename and delete group ([#1780](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1780)) ([bdf4c6a](https://github.com/RedHatInsights/insights-inventory-frontend/commit/bdf4c6ac30688e2f76f1e2e164e9e1438e255485)) - -# [1.8.0](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.7.2...v1.8.0) (2023-03-07) - - -### Features - -* **ESSNTL-4056:** Add lastSeen filter ([#1781](https://github.com/RedHatInsights/insights-inventory-frontend/issues/1781)) ([7bb6ac8](https://github.com/RedHatInsights/insights-inventory-frontend/commit/7bb6ac85fcb5fc7c31514066c2ef174868213687)) - ## [1.7.2](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.7.1...v1.7.2) (2023-03-07) ## [1.7.1](https://github.com/RedHatInsights/insights-inventory-frontend/compare/v1.7.0...v1.7.1) (2023-03-06) diff --git a/config/setupTests.js b/config/setupTests.js index a0e7943f6..5c2d52cef 100644 --- a/config/setupTests.js +++ b/config/setupTests.js @@ -15,34 +15,6 @@ jest.mock('react-router-dom', () => ({ }) })); -jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({ - __esModule: true, - default: () => ({ - updateDocumentTitle: jest.fn(), - auth: { - getUser: () => Promise.resolve({ - identity: { - account_number: '0', - type: 'User', - user: { - is_org_admin: true - } - }, - entitlements: { - hybrid_cloud: { is_entitled: true }, - insights: { is_entitled: true }, - openshift: { is_entitled: true }, - smart_management: { is_entitled: false } - } - }) - }, - appAction: jest.fn(), - appObjectId: jest.fn(), - on: jest.fn(), - getUserPermissions: () => Promise.resolve(['inventory:*:*']) - }) -})); - configure({ adapter: new Adapter() }); global.insights = { diff --git a/cypress/fixtures/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C.json b/cypress/fixtures/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C.json index 2f9ce9b03..e52cc1577 100644 --- a/cypress/fixtures/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C.json +++ b/cypress/fixtures/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C.json @@ -10,7 +10,28 @@ "account": "irure ea exercitation adipisicing velit", "org_id": "non", "created_at": "1994-07-28T22:00:00.0Z", - "host_ids": [] + "host_ids": [ + "eEfb7FAa-1f0b-Bde3-a4FF-fDdCd5faA764", + "bb7417faE7f9eCdacEDDd0Fcae9Cf4BB", + "2095BB72Aa6E1d2D1DC48bdecF1085eb", + "E645A3Fb42B77c4eBf9faEfCad8f6F5a", + "7fb4fa758Cbc0A861C2Cb21695aeA9d6", + "f6BE4AafA6bF543693Fa3F1fadFaA4E9", + "D608C33f-5e6D-BBEF-Cab5-0FFE8eB1bAc4", + "Dd4De9b6-7f3a-ED3d-2a84-D60e4Af2Cd9d", + "ae08E8dd-FFdB-cBC1-BA2A-d0aaC61C1B55", + "dcaD88bD4CeaaDC8bceD5d730ffba4cF", + "5a9F9CDAE74F3116a9c848Eeb1C65EA0", + "f53eDCBe3Fb957BE5dBec9322f030F94", + "c6fbBE38-30D7-D7e9-C8C7-72F4a61Bea9c", + "20E5EedA1e3f2aC2dCaADDCa4BFEED5B", + "adEcDf6ac4A999ccecdbCe7E9e01e23C", + "Bf0E2e8C-7e96-f0C8-9fea-1bD5fa9E18ce", + "6349ADdf-8d6B-bF1e-F5AE-13f0B7ca4acE", + "DBD5E149-C967-12b3-Cc6B-6c9220bA32e1", + "4EC19BbD-b556-ED9E-33E7-Eb5Be40AaeDe", + "CE05B39b3FdDad8dDE2b5DebB7CABe7D" + ] } ], "total": 1 diff --git a/cypress/fixtures/groups/Ba8B79ab5adC8E41e255D5f8aDb8f1F3.json b/cypress/fixtures/groups/Ba8B79ab5adC8E41e255D5f8aDb8f1F3.json deleted file mode 100644 index c499d9599..000000000 --- a/cypress/fixtures/groups/Ba8B79ab5adC8E41e255D5f8aDb8f1F3.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "count": 1, - "page": 1, - "per_page": 50, - "results": [ - { - "name": "ea velit incididunt", - "updated_at": "1998-04-17T22:00:00.0Z", - "id": "620f9ae75A8F6b83d78F3B55Af1c4b2C", - "account": "irure ea exercitation adipisicing velit", - "org_id": "non", - "created_at": "1994-07-28T22:00:00.0Z", - "host_ids": [ - "00fcC027-F4e3-aB4D-3eB9-392B09E4E9eC", - "82c882beA274db74bA79Dad9695CDdcD", - "374ECeB4-4aA6-637a-A5d8-eBD40A51EED8", - "12f6cdf2-fBcA-EF8A-3cC1-739627825F5B", - "37F4D8D9-A1Bd-cEE7-1Bcc-A03A20589B1B", - "ab29a822dA3259bf4a80A586Bd2e16E0", - "0ed9cfebEb3bfdB9A6F51757008dFB2F", - "4ddCF8Ee4a5DF968CBCaaaed16F5c55f", - "3cfF3e49-35Af-fD3E-62eb-c4eBDa0Ac3fF", - "8ce37289-4B8f-cECF-a3db-94f9F9ACB45a", - "Fd60B4f79D74507bDE8be6913caFDa8e", - "BbEAB14B-9A1f-eDFB-9c3B-eF0C6b6CB4dF", - "14FA22F08BBFf6f9738cBED71aFf66eE", - "fcF870cc-8C3C-8ba9-FcdA-dff79Ba0e2dE", - "38B60BDf17c9E6C9Cdd11840aCb4e804", - "C306bEc5EAF6BfD025fa6f48b3BeAF43", - "83CE42Eadccd8b2e5E5Aca1eaBAB55aa", - "4da9f2C5d6C1Ce6EE2f3D7BBC974E7c3", - "CaaE8C2e-C67c-41b3-5EB8-5Fd233eC9FdC" - ] - } - ], - "total": 1 -} diff --git a/cypress/fixtures/hosts.json b/cypress/fixtures/hosts.json deleted file mode 100644 index cd4c5c50b..000000000 --- a/cypress/fixtures/hosts.json +++ /dev/null @@ -1,2505 +0,0 @@ -{ - "count": 19, - "page": 1, - "per_page": 50, - "results": [ - { - "org_id": "velit dolore cillum ut veniam", - "ip_addresses": [ - "nulla eni", - "adipisicing Duis", - "commodo", - "c", - "ad ", - "dolore dolor", - "laboris eiusmod aliqua labore", - "veniam dolore dolor", - "amet dolore", - "sint", - "anim", - "irure", - "in", - "et lab", - "sed veniam", - "ut Excepteur", - "nisi occaecat sint amet", - "officia laborum dolor", - "commodo", - "anim dolor ullamco qui deserunt" - ], - "mac_addresses": null, - "facts": [ - { - "namespace": "et ali" - }, - { - "namespace": "aliqua ipsum laboris" - }, - { - "namespace": "dolore magna quis ipsum" - }, - { - "namespace": "eu proident aliqua et officia" - }, - { - "namespace": "sed labore tempor dolor" - }, - { - "namespace": "labore occaecat dol" - }, - { - "namespace": "sunt qui volupta" - }, - { - "namespace": "cupidatat Duis et velit dolor" - }, - { - "namespace": "voluptate occaecat" - }, - { - "namespace": "cillum adipisi" - }, - { - "namespace": "commodo" - }, - { - "namespace": "amet dolor aute" - }, - { - "namespace": "ex dese" - }, - { - "namespace": "consectetur mollit culpa dolore do" - }, - { - "namespace": "commodo ipsum cupidatat est anim" - }, - { - "namespace": "consectetur aliquip dolore cillum" - }, - { - "namespace": "consectetur ut nulla" - }, - { - "namespace": "volu" - }, - { - "namespace": "mollit" - }, - { - "namespace": "deserunt sit adipisicing in dolore" - } - ], - "reporter": "dolor in dolor officia", - "per_reporter_staleness": { - "aliquip__": { - "check_in_succeeded": false, - "last_check_in": "1987-10-30T00:00:00.0Z", - "stale_timestamp": "1977-07-20T23:00:00.0Z" - } - }, - "fqdn": null, - "updated": "1987-02-23T23:00:00.0Z", - "bios_uuid": null, - "subscription_manager_id": "dolor", - "provider_type": null, - "stale_timestamp": null, - "satellite_id": null, - "id": "dolor", - "stale_warning_timestamp": null, - "account": "Ut labore reprehenderit velit est", - "ansible_host": null, - "insights_id": "commodo nulla", - "provider_id": null, - "display_name": null, - "created": "2006-03-24T23:00:00.0Z", - "culled_timestamp": null - }, - { - "org_id": "consectetur elit", - "stale_timestamp": "2013-06-07T22:00:00.0Z", - "facts": [ - { - "namespace": "adipisicing" - }, - { - "namespace": "occaecat sunt" - }, - { - "namespace": "Ut labore est velit enim" - }, - { - "namespace": "cupidatat aliqua tempor amet" - }, - { - "namespace": "consequat enim Ut" - }, - { - "namespace": "incididunt" - }, - { - "namespace": "nulla do" - }, - { - "namespace": "proident tempor enim ut quis" - }, - { - "namespace": "laborum" - }, - { - "namespace": "aliqua deserunt" - }, - { - "namespace": "velit" - }, - { - "namespace": "et incididunt laboris non anim" - }, - { - "namespace": "do" - }, - { - "namespace": "est et ea " - }, - { - "namespace": "in" - }, - { - "namespace": "minim" - }, - { - "namespace": "fugiat Excepteur" - }, - { - "namespace": "dolore mollit qui" - }, - { - "namespace": "ad" - }, - { - "namespace": "ut sint" - } - ], - "ip_addresses": [ - "ullamco", - "culpa officia eiusmod voluptate quis", - "commodo non Excepteur dolore", - "consequat", - "cillum labor", - "nostrud amet deserunt dolor", - "reprehenderit", - "ea in nisi", - "in", - "fugiat adipisicing", - "sit est in qui", - "ea i", - "exercitation adipisic", - "sunt exercitation", - "fugiat sunt est dolore sint", - "eiusmod nulla aute", - "do deserunt", - "commodo nostrud cupidatat Excepteur id", - "non irure cillum minim", - "tempor nulla culpa non sunt" - ], - "display_name": null, - "updated": "1966-08-09T23:00:00.0Z", - "per_reporter_staleness": { - "eiusmodc2": { - "last_check_in": "1943-10-27T00:00:00.0Z", - "check_in_succeeded": true, - "stale_timestamp": "2012-12-31T23:00:00.0Z" - }, - "do_39f": { - "last_check_in": "1950-02-01T23:00:00.0Z", - "stale_timestamp": "1970-04-16T23:00:00.0Z", - "check_in_succeeded": false - }, - "labore7": { - "check_in_succeeded": true, - "last_check_in": "1981-06-13T22:00:00.0Z", - "stale_timestamp": "1945-09-29T22:00:00.0Z" - } - }, - "provider_type": null, - "stale_warning_timestamp": "2007-11-12T00:00:00.0Z", - "insights_id": null, - "reporter": null, - "satellite_id": "consequat occaecat Ut", - "subscription_manager_id": "magna ullamco in Ut", - "account": "dolore voluptate", - "mac_addresses": [ - "cupidatat nisi officia magna do", - "commodo ad", - "sint est id", - "irure eiusmod", - "ea magna aute occaecat dolor", - "aute voluptate", - "esse", - "nisi et", - "et aute qui nostrud in", - "dolore ut", - "ut veniam", - "mollit in eu aliqua", - "mollit in ut Lorem", - "amet", - "Duis qui sit adipisicing sunt", - "dolor", - "do sed", - "adipisicing in cupidatat", - "anim in consectetur sit", - "enim minim ex labore" - ], - "ansible_host": null, - "id": "aute", - "created": "1990-07-14T22:00:00.0Z", - "fqdn": null, - "provider_id": null, - "bios_uuid": "anim nulla ex eiusmod voluptate", - "culled_timestamp": null - }, - { - "org_id": "deserunt sit irure", - "updated": "1953-10-25T00:00:00.0Z", - "ip_addresses": [ - "exercitation id qui Lorem", - "ad Ut", - "incididunt aliquip Ut", - "voluptate nost", - "la", - "Duis consectetur do enim occaecat", - "laborum aliquip velit", - "mollit culpa", - "magna ex incididunt eu sed", - "adipisicing officia", - "magna proident pariatur ullamco", - "fugiat officia ullamco", - "Duis", - "ea ex quis id", - "eiusmod ex aliquip mollit pari", - "dolor ipsum laboris", - "sunt aliqua magna", - "dolor anim", - "in reprehenderit ut aute", - "" - ], - "id": "anim commodo", - "display_name": null, - "insights_id": null, - "provider_type": null, - "ansible_host": null, - "subscription_manager_id": "exercitation", - "reporter": "adipisicing veniam velit", - "created": "1962-06-25T23:00:00.0Z", - "account": null, - "group_name": "abc", - "mac_addresses": null, - "provider_id": "aute ut sit", - "facts": [ - { - "namespace": "consequat esse dolor" - }, - { - "namespace": "aliquip amet eiusmod ad tempor" - }, - { - "namespace": "fugi" - }, - { - "namespace": "nulla" - }, - { - "namespace": "qui proident et consequat mollit" - }, - { - "namespace": "reprehenderit eiusmod dolor" - }, - { - "namespace": "elit sed" - }, - { - "namespace": "non dolore in elit aute" - }, - { - "namespace": "f" - }, - { - "namespace": "quis" - }, - { - "namespace": "proident mollit enim" - }, - { - "namespace": "aliqua ipsum consectetur laborum ea" - }, - { - "namespace": "incididunt non fugiat eiusmod Ut" - }, - { - "namespace": "officia" - }, - { - "namespace": "laboris quis fugiat sint" - }, - { - "namespace": "labore cupida" - }, - { - "namespace": "quis aliqua d" - }, - { - "namespace": "minim ex" - }, - { - "namespace": "minim non in ut sunt" - }, - { - "namespace": "dolore consectetur do et minim" - } - ], - "stale_warning_timestamp": "1967-03-03T23:00:00.0Z", - "satellite_id": "magna cupidatat occaecat", - "bios_uuid": null, - "stale_timestamp": "1966-06-15T23:00:00.0Z", - "per_reporter_staleness": { - "qui_d0": { - "last_check_in": "1987-01-23T23:00:00.0Z", - "stale_timestamp": "2006-08-07T22:00:00.0Z", - "check_in_succeeded": false - } - }, - "culled_timestamp": null, - "fqdn": null - }, - { - "org_id": "fugiat in quis ea", - "stale_warning_timestamp": null, - "satellite_id": null, - "provider_id": null, - "created": "2008-11-15T00:00:00.0Z", - "insights_id": "dolor aliquip labo", - "display_name": "culpa ut Lorem aliquip", - "per_reporter_staleness": { - "nostruda36": { - "stale_timestamp": "2003-05-19T22:00:00.0Z", - "last_check_in": "1999-01-15T23:00:00.0Z", - "check_in_succeeded": false - }, - "ind7": { - "check_in_succeeded": true, - "stale_timestamp": "2015-04-26T22:00:00.0Z", - "last_check_in": "1966-07-10T23:00:00.0Z" - }, - "magna_73": { - "stale_timestamp": "2002-10-22T00:00:00.0Z", - "last_check_in": "2008-06-19T22:00:00.0Z", - "check_in_succeeded": true - } - }, - "reporter": null, - "updated": "1974-10-05T23:00:00.0Z", - "provider_type": "tempor cupidatat deserunt fugiat", - "bios_uuid": "qui nostrud aliqua consectetur", - "ip_addresses": [ - "reprehenderit Excepteur aliqua incididunt", - "dolor dolore ullamco fugiat", - "fugiat", - "eu sed ut Ut anim", - "occaecat", - "incididunt officia eu labore pro", - "esse tempor quis mollit cillum", - "exercitation dolore nostrud anim labo", - "elit cillum", - "incididunt sit qui", - "dolor", - "ut enim nulla ", - "aliquip id fugiat", - "ullamco id ut in", - "labore est laboris id do", - "proident sit Lorem", - "mollit", - "ipsum", - "quis", - "cillum esse" - ], - "ansible_host": "", - "stale_timestamp": null, - "subscription_manager_id": "ea sunt", - "facts": [ - { - "namespace": "Lorem enim" - }, - { - "namespace": "ipsum velit est enim dolore" - }, - { - "namespace": "Duis ea" - }, - { - "namespace": "elit ipsum" - }, - { - "namespace": "Duis dolore in" - }, - { - "namespace": "aliquip" - }, - { - "namespace": "consequat Lorem occaecat" - }, - { - "namespace": "cillum" - }, - { - "namespace": "Ut ad" - }, - { - "namespace": "nisi" - }, - { - "namespace": "eiusmod ea do ad adipisicing" - }, - { - "namespace": "in id incididunt" - }, - { - "namespace": "aliqua" - }, - { - "namespace": "Lorem fugiat" - }, - { - "namespace": "dolore laboris" - }, - { - "namespace": "ipsum" - }, - { - "namespace": "id sint culpa ea" - }, - { - "namespace": "in labore ullamco" - }, - { - "namespace": "nostrud dolore aliquip dolor" - }, - { - "namespace": "irure" - } - ], - "account": "commodo velit ad do", - "culled_timestamp": null, - "mac_addresses": [ - "commodo", - "minim", - "Duis eiusmod ut", - "pariatur", - "consequat mollit aliquip ullamco dolore", - "eu Ut Lorem ut fugiat", - "nisi ullamco", - "anim voluptate", - "Duis", - "aute", - "ali", - "magna", - "nostrud", - "consequat dolore do sint", - "anim in dolor pariatur incididunt", - "ex Lorem sed in", - "qui Duis cupidatat nisi exercitation", - "velit elit nostrud", - "qui Excepteur fugiat reprehe", - "nu" - ], - "fqdn": null, - "id": "ea qui dolor voluptate" - }, - { - "org_id": "ut do pariatur consequat", - "updated": "1945-11-22T00:00:00.0Z", - "per_reporter_staleness": { - "culpae": { - "check_in_succeeded": false, - "stale_timestamp": "1948-10-13T00:00:00.0Z", - "last_check_in": "1961-04-09T23:00:00.0Z" - }, - "et_5e3": { - "stale_timestamp": "1979-04-12T22:00:00.0Z", - "last_check_in": "1944-09-23T22:00:00.0Z", - "check_in_succeeded": false - }, - "eiusmod_8": { - "check_in_succeeded": true, - "stale_timestamp": "2008-09-21T22:00:00.0Z", - "last_check_in": "2009-05-16T22:00:00.0Z" - }, - "eube": { - "stale_timestamp": "2016-03-19T23:00:00.0Z", - "last_check_in": "1985-10-19T00:00:00.0Z", - "check_in_succeeded": true - }, - "Duis3c": { - "stale_timestamp": "1971-06-27T23:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "1987-08-17T22:00:00.0Z" - } - }, - "stale_timestamp": "1995-03-17T23:00:00.0Z", - "facts": [ - { - "namespace": "Excepteur in quis" - }, - { - "namespace": "enim quis" - }, - { - "namespace": "Lorem sit" - }, - { - "namespace": "reprehenderit ea nulla anim incididunt" - }, - { - "namespace": "veniam ut tempor anim laboris" - }, - { - "namespace": "quis deserunt" - }, - { - "namespace": "ex Lorem ut adipisicing" - }, - { - "namespace": "aute elit" - }, - { - "namespace": "sed culpa" - }, - { - "namespace": "ad mollit non nisi occaecat" - }, - { - "namespace": "amet ut do" - }, - { - "namespace": "esse sed" - }, - { - "namespace": "laboris" - }, - { - "namespace": "sit" - }, - { - "namespace": "volupta" - }, - { - "namespace": "sunt ipsum cupidatat commodo" - }, - { - "namespace": "enim aliquip cillum" - }, - { - "namespace": "ad Duis" - }, - { - "namespace": "consectetur aute veniam esse" - }, - { - "namespace": "cillum commodo consequat fugiat in" - } - ], - "satellite_id": "adipisicing fugiat irur", - "provider_id": "tempor", - "culled_timestamp": "2008-09-27T22:00:00.0Z", - "created": "1955-09-28T23:00:00.0Z", - "fqdn": null, - "display_name": "in consec", - "insights_id": null, - "mac_addresses": [ - "do", - "nisi pariatu", - "dolore magna", - "in", - "eiusm", - "aliquip ut nisi", - "proident", - "occaecat ", - "tempor", - "consec", - "ad incididunt aute", - "quis anim", - "consectetur cupidatat", - "ad do", - "sed", - "nulla cillum Duis sed", - "dolor ea Ut", - "", - "incididunt nulla sed officia consectet", - "in amet aliquip Excepteur magna" - ], - "account": null, - "reporter": "sit amet Ut", - "subscription_manager_id": "officia", - "provider_type": "veniam ipsum commodo in", - "ansible_host": null, - "id": "consectetur", - "stale_warning_timestamp": "1988-07-25T22:00:00.0Z", - "bios_uuid": null, - "ip_addresses": [ - "ipsum ", - "minim reprehenderit ad et", - "est laborum", - "Ut in", - "esse culpa", - "laboris non", - "sunt amet dolor cillum", - "nostrud eiusmod", - "veniam non dolor aliqua", - "et consequat", - "aliqua nulla consequat", - "esse eiusmod est", - "non sed incididunt sunt aliquip", - "id commodo", - "nisi qui deserunt", - "Lorem sit qui dolor", - "ullamco", - "Duis dolore dolore temp", - "enim ut in ipsum Excepteur", - "ut dolor consequat aliquip ea" - ] - }, - { - "org_id": "irure magna est ipsum commodo", - "account": null, - "per_reporter_staleness": { - "tempor4f": { - "check_in_succeeded": true, - "last_check_in": "1983-06-05T22:00:00.0Z", - "stale_timestamp": "1949-05-04T22:00:00.0Z" - }, - "nisi260": { - "stale_timestamp": "2011-06-10T22:00:00.0Z", - "last_check_in": "1977-09-11T23:00:00.0Z", - "check_in_succeeded": false - } - }, - "updated": "1997-10-13T00:00:00.0Z", - "bios_uuid": null, - "facts": [ - { - "namespace": "dolore culpa ullamco" - }, - { - "namespace": "commodo est eu nulla ut" - }, - { - "namespace": "sed exercitation in" - }, - { - "namespace": "labore qui fugiat sunt" - }, - { - "namespace": "id aliquip" - }, - { - "namespace": "deserunt" - }, - { - "namespace": "culpa enim" - }, - { - "namespace": "commodo" - }, - { - "namespace": "irure incididunt nostrud proident" - }, - { - "namespace": "ullamco s" - }, - { - "namespace": "mollit incididunt aliquip Lorem" - }, - { - "namespace": "et" - }, - { - "namespace": "ut do ir" - }, - { - "namespace": "culpa adipisicing" - }, - { - "namespace": "consectetur ad magna sit enim" - }, - { - "namespace": "cupidatat" - }, - { - "namespace": "d" - }, - { - "namespace": "sed dolor ad labore veniam" - }, - { - "namespace": "ut" - }, - { - "namespace": "Ut" - } - ], - "id": "quis enim et", - "culled_timestamp": null, - "fqdn": "cupidatat nisi ut", - "stale_warning_timestamp": null, - "reporter": "dolor ex ullamco", - "mac_addresses": [ - "cupidatat", - "exercitation ut sed officia", - "reprehenderit do", - "incididunt tempor Ut consequat exerci", - "dolor eu", - "do aliqua", - "ipsum in", - "culpa dolore", - "et amet nostrud elit", - "velit dolore anim", - "officia ipsum", - "adipisicing veniam nostrud", - "Excepteur", - "pariatur voluptate Lorem", - "esse aliquip", - "aute officia eiusmod Duis", - "elit ut minim consequat", - "cillum ad mollit", - "tempor et cillum aute in", - "eu nisi" - ], - "provider_type": "laboris id labore occaecat aliq", - "subscription_manager_id": "proident ut nulla minim fugiat", - "ip_addresses": null, - "satellite_id": "reprehenderit incididunt ea dolor", - "display_name": null, - "stale_timestamp": null, - "ansible_host": null, - "created": "2016-06-02T22:00:00.0Z", - "insights_id": null, - "provider_id": "ipsum Ut id tempor cupid" - }, - { - "org_id": "t", - "facts": [ - { - "namespace": "in in sed" - }, - { - "namespace": "nisi incididunt ullamco quis" - }, - { - "namespace": "elit tempor sunt" - }, - { - "namespace": "elit" - }, - { - "namespace": "deserunt proident no" - }, - { - "namespace": "laboris nostrud" - }, - { - "namespace": "Lorem" - }, - { - "namespace": "proident officia laboris" - }, - { - "namespace": "sit Lorem" - }, - { - "namespace": "amet velit" - }, - { - "namespace": "mollit Duis" - }, - { - "namespace": "magna Lorem eiusmod mo" - }, - { - "namespace": "o" - }, - { - "namespace": "esse dolor" - }, - { - "namespace": "ad enim" - }, - { - "namespace": "dolore adipisicing Duis" - }, - { - "namespace": "minim nulla est ea" - }, - { - "namespace": "est elit" - }, - { - "namespace": "et" - }, - { - "namespace": "dolore adipisicin" - } - ], - "bios_uuid": "ea ipsum eu voluptate", - "stale_timestamp": "2008-01-15T23:00:00.0Z", - "insights_id": "aliquip labore", - "mac_addresses": null, - "display_name": "ven", - "created": "1995-09-16T22:00:00.0Z", - "per_reporter_staleness": { - "ea9e": { - "last_check_in": "1999-10-20T00:00:00.0Z", - "check_in_succeeded": false, - "stale_timestamp": "1955-07-02T23:00:00.0Z" - }, - "et_4f": { - "last_check_in": "1952-04-02T23:00:00.0Z", - "stale_timestamp": "1973-06-27T23:00:00.0Z", - "check_in_succeeded": true - } - }, - "fqdn": null, - "stale_warning_timestamp": null, - "ansible_host": "non minim sint et", - "subscription_manager_id": null, - "provider_id": "aute tempor", - "ip_addresses": [ - "ut sunt", - "nisi", - "proident exercitation labore elit consequat", - "mollit", - "incididunt qui", - "eiusmod veniam qui magna ut", - "in irure", - "dolore proident reprehenderit", - "ipsu", - "veniam adipisicing officia", - "dolor et dolore", - "enim dolor", - "exercitation laborum id deserunt", - "quis sed nostrud volu", - "amet nulla", - "ex", - "minim amet", - "esse est voluptate", - "labore velit", - "cupidatat" - ], - "reporter": null, - "satellite_id": null, - "account": "aliquip", - "provider_type": "in", - "id": "in", - "updated": "2004-12-06T23:00:00.0Z", - "culled_timestamp": null - }, - { - "org_id": "sit sint", - "provider_id": "est ad do", - "provider_type": "in", - "created": "1956-01-15T23:00:00.0Z", - "ip_addresses": null, - "culled_timestamp": "1977-08-01T23:00:00.0Z", - "satellite_id": null, - "updated": "1971-03-27T23:00:00.0Z", - "stale_timestamp": "2017-05-15T22:00:00.0Z", - "bios_uuid": "Excepteur non", - "subscription_manager_id": null, - "reporter": null, - "account": null, - "id": "adipisicing Ut non ut", - "fqdn": "Lorem ex est a", - "stale_warning_timestamp": null, - "ansible_host": null, - "per_reporter_staleness": { - "tempor5d1": { - "stale_timestamp": "1985-04-23T22:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "2003-04-02T22:00:00.0Z" - }, - "quis_cc": { - "check_in_succeeded": false, - "stale_timestamp": "2013-07-13T22:00:00.0Z", - "last_check_in": "1958-06-16T23:00:00.0Z" - } - }, - "facts": [ - { - "namespace": "nisi" - }, - { - "namespace": "non" - }, - { - "namespace": "et dolore" - }, - { - "namespace": "est laboris sed exercitation Excepteur" - }, - { - "namespace": "dolor" - }, - { - "namespace": "cupidatat dolore" - }, - { - "namespace": "do in aliqua cupidata" - }, - { - "namespace": "ullamco" - }, - { - "namespace": "cupidatat" - }, - { - "namespace": "enim labore" - }, - { - "namespace": "et" - }, - { - "namespace": "i" - }, - { - "namespace": "sint id cillum adipisicing" - }, - { - "namespace": "ut" - }, - { - "namespace": "e" - }, - { - "namespace": "quis" - }, - { - "namespace": "qui" - }, - { - "namespace": "Lorem magna sit tem" - }, - { - "namespace": "laboris aute ad minim" - }, - { - "namespace": "commodo do deserunt nostrud" - } - ], - "display_name": null, - "mac_addresses": null, - "insights_id": "in id fugiat dolor quis" - }, - { - "org_id": "est sunt nulla voluptate r", - "ip_addresses": [ - "nostrud occaecat dolor", - "dolore", - "ipsum vel", - "velit culpa commodo", - "aliqua", - "cillum ", - "tempor est do eiusmod elit", - "laboris incididunt dolor dolore laborum", - "velit elit commodo", - "aliquip sint sunt mollit ea", - "labore pariatur anim", - "dolore aute ut ipsum reprehenderit", - "dolor aute ea", - "dolore et ipsum", - "Lorem eiusmod tempor", - "ex ea", - "dolor pariatur", - "ex Duis", - "pariatur", - "reprehenderit quis cupidatat" - ], - "display_name": "pariatur Duis exercitation occaecat", - "facts": [ - { - "namespace": "cillum fugiat" - }, - { - "namespace": "ea nisi ut aut" - }, - { - "namespace": "quis" - }, - { - "namespace": "non sunt" - }, - { - "namespace": "dol" - }, - { - "namespace": "consequat aliquip esse ex" - }, - { - "namespace": "cupidatat pariatur" - }, - { - "namespace": "id sit cupidatat ut oc" - }, - { - "namespace": "aliqua esse" - }, - { - "namespace": "do" - }, - { - "namespace": "aliqua" - }, - { - "namespace": "ut id" - }, - { - "namespace": "ea labore in sed dolor" - }, - { - "namespace": "dolor labore consectetur quis" - }, - { - "namespace": "Excepteur" - }, - { - "namespace": "labore" - }, - { - "namespace": "in" - }, - { - "namespace": "veniam consectetur magna nisi fug" - }, - { - "namespace": "in sunt magna et aliqua" - }, - { - "namespace": "reprehenderit Ut" - } - ], - "stale_timestamp": null, - "provider_type": "est laboris", - "account": "ut culpa", - "updated": "1962-06-27T23:00:00.0Z", - "mac_addresses": null, - "provider_id": null, - "bios_uuid": null, - "ansible_host": null, - "stale_warning_timestamp": "1992-12-08T23:00:00.0Z", - "created": "2010-03-15T23:00:00.0Z", - "insights_id": null, - "per_reporter_staleness": { - "dolor_fc": { - "stale_timestamp": "1948-05-23T22:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "1969-11-22T00:00:00.0Z" - }, - "cillumd81": { - "last_check_in": "1983-11-26T00:00:00.0Z", - "stale_timestamp": "2002-02-08T23:00:00.0Z", - "check_in_succeeded": true - } - }, - "id": "mollit pariatur elit laborum", - "satellite_id": "ut magna in anim", - "fqdn": null, - "culled_timestamp": "1989-12-30T00:00:00.0Z", - "reporter": "do i", - "subscription_manager_id": null - }, - { - "org_id": "ipsum ", - "account": "do officia aute ut laborum", - "fqdn": "dolore nulla dolor", - "id": "elit magna culpa", - "bios_uuid": null, - "mac_addresses": [ - "quis esse eu sit minim", - "fugiat", - "dolore voluptate", - "Lorem Duis ullamco", - "eu aliqua", - "aliqua dolore", - "ut null", - "Excepteur esse dolore et ullamco", - "in minim", - "dolor", - "exerc", - "officia", - "minim proident velit enim pariatur", - "commodo laborum", - "pariatur", - "sint mollit Excepteur eiusmod officia", - "ullamco aliquip sed", - "dolore et", - "dolore esse", - "dolore Excepteur eiusmod minim" - ], - "ansible_host": null, - "reporter": "aliqua proident amet quis sin", - "satellite_id": null, - "stale_timestamp": null, - "created": "2013-09-10T22:00:00.0Z", - "updated": "1976-07-14T23:00:00.0Z", - "display_name": null, - "ip_addresses": null, - "insights_id": null, - "facts": [ - { - "namespace": "id" - }, - { - "namespace": "cupidatat non l" - }, - { - "namespace": "dolore" - }, - { - "namespace": "Lor" - }, - { - "namespace": "labore nisi" - }, - { - "namespace": "ullamco in dese" - }, - { - "namespace": "dolor fugiat laborum aliquip qui" - }, - { - "namespace": "cillum non" - }, - { - "namespace": "molli" - }, - { - "namespace": "Ut" - }, - { - "namespace": "Duis dolore incididunt est dolor" - }, - { - "namespace": "officia ut id pr" - }, - { - "namespace": "enim reprehenderit c" - }, - { - "namespace": "sed esse commodo" - }, - { - "namespace": "fugiat sit" - }, - { - "namespace": "cupidatat pariatur nisi Excepteur" - }, - { - "namespace": "consequat ipsum nostrud voluptate dolore" - }, - { - "namespace": "laboris sed" - }, - { - "namespace": "incididunt mollit amet" - }, - { - "namespace": "ut labore et proident" - } - ], - "culled_timestamp": "1955-12-25T00:00:00.0Z", - "stale_warning_timestamp": null, - "per_reporter_staleness": { - "Excepteur_a90": { - "check_in_succeeded": true, - "last_check_in": "1952-03-05T23:00:00.0Z", - "stale_timestamp": "2013-07-04T22:00:00.0Z" - } - }, - "subscription_manager_id": "in ea ut tempor", - "provider_type": "Ut incididunt sit officia mollit", - "provider_id": null - }, - { - "org_id": "aliqua labore velit veniam dolore", - "stale_warning_timestamp": null, - "mac_addresses": null, - "per_reporter_staleness": { - "labore_84": { - "stale_timestamp": "1952-07-20T23:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "2006-09-08T22:00:00.0Z" - }, - "voluptate_1": { - "stale_timestamp": "1990-08-20T22:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "1957-05-01T23:00:00.0Z" - } - }, - "display_name": "sit dolor eiusmod", - "satellite_id": null, - "ansible_host": null, - "updated": "1980-04-01T23:00:00.0Z", - "insights_id": null, - "subscription_manager_id": null, - "id": "aute", - "facts": [ - { - "namespace": "in" - }, - { - "namespace": "sed mollit nisi ad" - }, - { - "namespace": "in cillum non" - }, - { - "namespace": "mollit aliquip nisi cillum" - }, - { - "namespace": "laboris amet cupidatat ut" - }, - { - "namespace": "adipisicing est" - }, - { - "namespace": "magna cillum" - }, - { - "namespace": "fugiat in" - }, - { - "namespace": "in incididunt Duis minim" - }, - { - "namespace": "dolore in elit " - }, - { - "namespace": "in eu elit" - }, - { - "namespace": "dolore do est" - }, - { - "namespace": "ut cillum deserunt minim dolore" - }, - { - "namespace": "commodo " - }, - { - "namespace": "adipisi" - }, - { - "namespace": "quis ex pariatur" - }, - { - "namespace": "culpa consequat Ut consectetur" - }, - { - "namespace": "exercitation est ad" - }, - { - "namespace": "anim adipisicing occaecat reprehenderit veniam" - }, - { - "namespace": "labore cupidatat Duis" - } - ], - "account": "Duis Excepteur sint magna", - "ip_addresses": [ - "sunt adipisicin", - "sunt Duis irure", - "aliquip enim et ut ali", - "proident aliquip", - "aliquip laborum adipisicing", - "fugiat anim qui", - "ut proident", - "tempor dolore", - "cillum", - "quis", - "incididunt dolore in deserun", - "reprehenderit esse Ut", - "Ut mollit dolor", - "veniam fugiat laborum minim ips", - "officia veniam in reprehenderit ipsum", - "Excepteur cupidatat Lorem ea", - "Ut mollit fugiat culpa", - "commodo qui velit", - "adipisicing est in ut sit", - "fugiat labore nulla est laboris" - ], - "provider_type": null, - "bios_uuid": null, - "reporter": "ad Excep", - "fqdn": "officia non ", - "culled_timestamp": "1971-03-30T23:00:00.0Z", - "provider_id": "off", - "created": "2008-08-15T22:00:00.0Z", - "stale_timestamp": "2001-07-05T22:00:00.0Z" - }, - { - "org_id": "irure anim labore ut", - "account": "aliqua adipisicing", - "id": "voluptate velit", - "culled_timestamp": null, - "subscription_manager_id": null, - "ansible_host": "conse", - "stale_warning_timestamp": "1987-03-16T23:00:00.0Z", - "updated": "1977-06-12T23:00:00.0Z", - "reporter": null, - "insights_id": "dolore voluptate in Ut", - "bios_uuid": "in occaecat", - "per_reporter_staleness": { - "ullamco1": { - "last_check_in": "1964-07-29T23:00:00.0Z", - "stale_timestamp": "1997-05-19T22:00:00.0Z", - "check_in_succeeded": true - }, - "qui_c48": { - "last_check_in": "1976-03-01T23:00:00.0Z", - "check_in_succeeded": false, - "stale_timestamp": "2009-11-25T00:00:00.0Z" - } - }, - "provider_id": "in", - "stale_timestamp": "1985-10-31T00:00:00.0Z", - "display_name": null, - "created": "2000-02-21T23:00:00.0Z", - "fqdn": "ex quis cupida", - "facts": [ - { - "namespace": "non" - }, - { - "namespace": "aute" - }, - { - "namespace": "amet Excepteur" - }, - { - "namespace": "culpa ex" - }, - { - "namespace": "eiusmod pr" - }, - { - "namespace": "sit ad" - }, - { - "namespace": "mollit pariatur veniam dolore" - }, - { - "namespace": "nulla id" - }, - { - "namespace": "sit dolor sunt non" - }, - { - "namespace": "ipsum occaecat" - }, - { - "namespace": "minim" - }, - { - "namespace": "labore quis" - }, - { - "namespace": "Ut cupidatat commodo est" - }, - { - "namespace": "est ad" - }, - { - "namespace": "nulla" - }, - { - "namespace": "sunt veniam exercit" - }, - { - "namespace": "ex laborum dolor do" - }, - { - "namespace": "cupidatat eiusmod nisi" - }, - { - "namespace": "cillum quis qui irure ipsum" - }, - { - "namespace": "eu" - } - ], - "satellite_id": "minim ad Duis tempor cillum", - "mac_addresses": [ - "nisi eiusmod", - "eiusmod non", - "minim anim nostr", - "culpa Ut", - "ad eu commodo laborum sed", - "eu labore officia", - "ex sunt aliquip quis", - "consequat irure esse minim", - "culpa in Ut deserunt", - "commodo aliquip", - "cillum incididunt dolor cupidatat sit", - "ut", - "cillum dolore", - "enim nostr", - "nostrud commodo", - "labore dolore", - "in", - "exercitation aliqua", - "aliqua", - "consectetur" - ], - "ip_addresses": [ - "ipsum sunt", - "esse eiusmod dolore tempor", - "consequat", - "amet est", - "cupidatat", - "do aute", - "aliquip consectetur sunt", - "et", - "eiusmod", - "non laborum cillum nostrud in", - "est cupidatat elit dolor", - "nostrud", - "qui ea laborum enim", - "amet id proident culpa reprehenderit", - "fugiat Lorem deserunt do", - "consequat Duis aliqua in", - "labor", - "volupta", - "aliquip pariatur voluptate o", - "veniam ut dolore" - ], - "provider_type": null - }, - { - "org_id": "nostrud ullamco sed dolor", - "provider_id": "officia do", - "satellite_id": null, - "ip_addresses": [ - "officia commodo ipsum laboris", - "mollit ex veniam dolor", - "ipsu", - "pariatur ut", - "velit Lorem laborum non", - "nisi labore", - "nulla cupidatat aute", - "aute", - "reprehenderit", - "eu amet", - "magna nulla ea ad", - "dolore", - "magna exercitation", - "aliqua", - "sed dolor adipisicing", - "culpa quis non", - "ad esse", - "ad ex occaecat minim anim", - "eu in cupidatat consectetur irure", - "occaecat eiusmo" - ], - "insights_id": "exercitation et in nostrud", - "ansible_host": null, - "facts": [ - { - "namespace": "cupidatat in aliquip pariatur sed" - }, - { - "namespace": "quis ea culpa aute" - }, - { - "namespace": "do" - }, - { - "namespace": "ad dolor reprehenderit et dolore" - }, - { - "namespace": "enim" - }, - { - "namespace": "magna adipisicing ex aute qui" - }, - { - "namespace": "sunt pa" - }, - { - "namespace": "laboris" - }, - { - "namespace": "dolor elit cupidatat" - }, - { - "namespace": "et" - }, - { - "namespace": "et amet" - }, - { - "namespace": "elit ea" - }, - { - "namespace": "in proident" - }, - { - "namespace": "deserunt do mollit esse" - }, - { - "namespace": "volupta" - }, - { - "namespace": "in minim Ut sed" - }, - { - "namespace": "aute incididu" - }, - { - "namespace": "cupidatat" - }, - { - "namespace": "proident elit" - }, - { - "namespace": "dolore reprehenderit sint" - } - ], - "fqdn": "ad", - "bios_uuid": null, - "per_reporter_staleness": { - "tempor_18": { - "check_in_succeeded": true, - "stale_timestamp": "2016-10-13T00:00:00.0Z", - "last_check_in": "1957-02-05T23:00:00.0Z" - }, - "eiusmod_2": { - "stale_timestamp": "1965-12-23T00:00:00.0Z", - "last_check_in": "1955-12-13T00:00:00.0Z", - "check_in_succeeded": true - }, - "deserunt_fd": { - "check_in_succeeded": false, - "last_check_in": "1946-08-20T22:00:00.0Z", - "stale_timestamp": "1993-01-13T23:00:00.0Z" - } - }, - "account": null, - "created": "1962-09-26T23:00:00.0Z", - "reporter": null, - "provider_type": null, - "updated": "1971-07-15T23:00:00.0Z", - "stale_timestamp": "1998-01-11T23:00:00.0Z", - "id": "elit laboris velit ullamco non", - "mac_addresses": [ - "qui", - "tempor irure", - "incid", - "mollit voluptate", - "sint aliqua", - "proident", - "i", - "laborum ut cillum in", - "do qui", - "incididunt aute", - "in", - "commodo Lor", - "dolore aliqua laboris", - "tempor dolor do ut eu", - "magna officia in enim ut", - "non culpa", - "quis anim fugiat", - "nisi pariatur culpa consectetur", - "id sunt elit Duis", - "Ut eu" - ], - "culled_timestamp": "2008-02-13T23:00:00.0Z", - "stale_warning_timestamp": null, - "subscription_manager_id": null, - "display_name": "nostrud dolor cillum elit velit" - }, - { - "org_id": "amet cillum laboris velit enim", - "created": "1975-10-10T00:00:00.0Z", - "fqdn": "officia id", - "id": "dolore aut", - "bios_uuid": null, - "stale_warning_timestamp": "2011-11-15T00:00:00.0Z", - "provider_id": "in aliqua ut Lorem", - "per_reporter_staleness": { - "dolore_8": { - "stale_timestamp": "2000-07-18T22:00:00.0Z", - "check_in_succeeded": true, - "last_check_in": "1971-05-12T23:00:00.0Z" - }, - "eiusmod8": { - "last_check_in": "2009-10-10T00:00:00.0Z", - "stale_timestamp": "1955-05-29T23:00:00.0Z", - "check_in_succeeded": false - }, - "Loremddf": { - "stale_timestamp": "1948-01-23T23:00:00.0Z", - "last_check_in": "1950-08-29T23:00:00.0Z", - "check_in_succeeded": true - } - }, - "provider_type": null, - "display_name": null, - "mac_addresses": [ - "ex", - "dolor dolor", - "reprehenderit pariatur", - "aliqua sed Excepteur sit Lorem", - "est consequat", - "minim ", - "anim", - "commodo esse sint adipisicing", - "et culpa enim sint dolor", - "deserunt eu", - "ullamco sit", - "nostrud", - "Ut culpa est", - "sint magna", - "Excepteur nulla consectetur", - "Excepteur occaecat est", - "Lorem ex fugiat", - "ex exercitation voluptate", - "cillum", - "Ut amet ex deserunt" - ], - "account": null, - "ansible_host": "sed do consequa", - "satellite_id": "dolore", - "insights_id": null, - "reporter": "eiusmod mollit sit proi", - "ip_addresses": null, - "stale_timestamp": null, - "culled_timestamp": "1944-11-17T00:00:00.0Z", - "updated": "1955-12-02T23:00:00.0Z", - "facts": [ - { - "namespace": "dolor commodo nulla" - }, - { - "namespace": "sunt in ad ipsum" - }, - { - "namespace": "sit nostrud tempor non" - }, - { - "namespace": "eu laboris co" - }, - { - "namespace": "consequat sint laboris officia nulla" - }, - { - "namespace": "qui tempor enim" - }, - { - "namespace": "qui eu dolore consectetur" - }, - { - "namespace": "proident exercitation id in" - }, - { - "namespace": "fugiat sunt nostrud id" - }, - { - "namespace": "ea consequat" - }, - { - "namespace": "laborum sunt quis" - }, - { - "namespace": "irure fugiat id mollit tempor" - }, - { - "namespace": "id anim" - }, - { - "namespace": "sunt aliq" - }, - { - "namespace": "ex esse in" - }, - { - "namespace": "anim non est Duis" - }, - { - "namespace": "magna pariatur laboris nisi" - }, - { - "namespace": "exercitation labore dolor pariatur voluptate" - }, - { - "namespace": "Excepteu" - }, - { - "namespace": "cillum" - } - ], - "subscription_manager_id": null - }, - { - "org_id": "ex commodo ", - "provider_type": "ad qui Duis ", - "mac_addresses": [ - "in es", - "esse dolor", - "dolore et", - "cillum sit", - "occaecat", - "pariatur irure", - "proident consectetur culpa irure", - "in elit fugiat ut", - "tempor laborum est voluptate", - "dolore dolor n", - "dolor fugiat aute mollit", - "nulla ut ad sed sunt", - "do officia", - "anim", - "sed adipisicing", - "adipisicing amet sit aliqua", - "laboris anim nostrud do", - "laborum exercitation", - "o", - "commodo fugiat" - ], - "stale_warning_timestamp": null, - "insights_id": "cupidatat ut", - "culled_timestamp": null, - "display_name": null, - "updated": "2002-09-04T22:00:00.0Z", - "bios_uuid": null, - "ip_addresses": [ - "velit", - "aute consectetur voluptate adipisicing", - "aliqua", - "consequat magna et commodo exercitation", - "deserunt fugiat esse", - "sit mollit est", - "veniam est", - "culpa occaecat id", - "deserunt enim", - "adipisicing", - "est exercitation ullamco", - "officia culpa aliqua", - "fugiat anim eu voluptate Ut", - "in dolor reprehenderit Duis enim", - "pariatur", - "Duis sed cillum tempor in", - "do", - "d", - "id laborum commodo", - "velit" - ], - "satellite_id": "pariatur consequat Lorem enim officia", - "provider_id": "mollit", - "facts": [ - { - "namespace": "deserunt ipsum " - }, - { - "namespace": "aliqua est" - }, - { - "namespace": "proident" - }, - { - "namespace": "magna irure consectetur" - }, - { - "namespace": "consectetur non commodo ut" - }, - { - "namespace": "in qui aute magna in" - }, - { - "namespace": "Duis reprehenderit deserunt est" - }, - { - "namespace": "in exercitation nulla" - }, - { - "namespace": "cillum nulla" - }, - { - "namespace": "deserunt elit fugiat est" - }, - { - "namespace": "non" - }, - { - "namespace": "magna" - }, - { - "namespace": "a" - }, - { - "namespace": "eu" - }, - { - "namespace": "magna nostrud incididunt" - }, - { - "namespace": "mollit qui" - }, - { - "namespace": "in tempor et pariatur" - }, - { - "namespace": "tempor" - }, - { - "namespace": "in" - }, - { - "namespace": "proident dese" - } - ], - "created": "1979-10-29T00:00:00.0Z", - "per_reporter_staleness": { - "dolor81b": { - "check_in_succeeded": true, - "last_check_in": "1949-01-14T23:00:00.0Z", - "stale_timestamp": "1988-04-18T22:00:00.0Z" - } - }, - "subscription_manager_id": "in dolor nostrud occaecat i", - "fqdn": "veniam ipsum", - "account": null, - "stale_timestamp": null, - "reporter": "ex ut ad nostrud", - "id": "eu quis nostrud", - "ansible_host": null - }, - { - "org_id": "irure consectetur cillum Excepteur", - "id": "reprehenderit et non dolore", - "provider_id": null, - "account": null, - "per_reporter_staleness": { - "in0b": { - "stale_timestamp": "1975-09-10T23:00:00.0Z", - "check_in_succeeded": true, - "last_check_in": "1984-03-26T22:00:00.0Z" - }, - "mollit9e": { - "stale_timestamp": "1960-11-03T23:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "1962-02-02T23:00:00.0Z" - }, - "cupidatat1e": { - "stale_timestamp": "1999-02-10T23:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "1984-09-05T22:00:00.0Z" - }, - "pariatur32e": { - "stale_timestamp": "2001-04-20T22:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "2006-01-12T23:00:00.0Z" - } - }, - "ansible_host": "deserunt velit", - "satellite_id": "cupidatat aute dolor fugiat nulla", - "created": "1991-09-26T22:00:00.0Z", - "facts": [ - { - "namespace": "quis ut ut irure" - }, - { - "namespace": "quis ut con" - }, - { - "namespace": "qui" - }, - { - "namespace": "non ea irure consequat" - }, - { - "namespace": "do" - }, - { - "namespace": "dolore" - }, - { - "namespace": "sint dolor qui" - }, - { - "namespace": "do tempor aute" - }, - { - "namespace": "sint eiusmod consequat fugia" - }, - { - "namespace": "voluptate laboris sed consequat do" - }, - { - "namespace": "pariatur tempor voluptate proident aliqua" - }, - { - "namespace": "adipisicing sunt consectetur" - }, - { - "namespace": "consequat ut sed et" - }, - { - "namespace": "esse in quis" - }, - { - "namespace": "pariatur Lorem ut" - }, - { - "namespace": "exercitation " - }, - { - "namespace": "reprehenderit eu sed id esse" - }, - { - "namespace": "in ad quis nulla" - }, - { - "namespace": "dolore sed magna laboris" - }, - { - "namespace": "ullamco aute" - } - ], - "culled_timestamp": null, - "stale_timestamp": null, - "insights_id": "tempor d", - "reporter": "velit occaecat", - "updated": "1960-03-18T23:00:00.0Z", - "provider_type": "aute sunt exercitation", - "display_name": "enim do", - "ip_addresses": null, - "mac_addresses": [ - "qui", - "dolor non in sed ipsum", - "cillum dolor", - "cillum voluptate id ex officia", - "magna", - "in est consectetur sed", - "sit fugiat ea aute veniam", - "voluptate", - "voluptate", - "Excepteur laboris Lorem Ut", - "sunt sed nostrud sit qui", - "in eiusmod", - "si", - "in Ut cupidatat eu", - "aute ut", - "velit", - "occaecat", - "eu velit pariatur", - "sed adipisicing consequat", - "laborum mollit" - ], - "subscription_manager_id": null, - "bios_uuid": "minim enim cillum quis", - "fqdn": null, - "stale_warning_timestamp": "1998-10-05T22:00:00.0Z" - }, - { - "org_id": "Duis magna do", - "provider_id": null, - "facts": [ - { - "namespace": "sunt mollit ea" - }, - { - "namespace": "ut Duis eu cupidatat ea" - }, - { - "namespace": "amet in ad" - }, - { - "namespace": "ad esse est" - }, - { - "namespace": "quis nisi pariatur" - }, - { - "namespace": "est ex sunt" - }, - { - "namespace": "dolor Lorem cons" - }, - { - "namespace": "dolore" - }, - { - "namespace": "officia elit eu velit" - }, - { - "namespace": "dolor qui" - }, - { - "namespace": "consectetur magna proident voluptate" - }, - { - "namespace": "eu eiusmod mollit sit" - }, - { - "namespace": "proident cupidatat aute culpa id" - }, - { - "namespace": "officia dolor consequat esse" - }, - { - "namespace": "ut proident elit in minim" - }, - { - "namespace": "consectet" - }, - { - "namespace": "ad amet eu tempor" - }, - { - "namespace": "elit in eu exercitation non" - }, - { - "namespace": "Excepteur tempor et Lorem exercitati" - }, - { - "namespace": "eiusmod mollit in pariatur" - } - ], - "updated": "1946-11-14T00:00:00.0Z", - "reporter": null, - "id": "dolore amet", - "ip_addresses": [ - "incididunt", - "adipisicing", - "velit nulla", - "elit ex in minim", - "consectetur quis", - "", - "mollit ullamco id", - "voluptate ut occaecat", - "sint", - "Lorem", - "in ea ", - "ut consequa", - "velit non a", - "qui dolor Duis fugiat", - "tempor pariatur elit ut laborum", - "pariatur", - "esse", - "sint", - "sint in id elit velit", - "Duis quis proident" - ], - "stale_warning_timestamp": "1973-04-13T23:00:00.0Z", - "provider_type": "nulla magna", - "per_reporter_staleness": { - "nulla_8": { - "check_in_succeeded": true, - "stale_timestamp": "1946-08-27T22:00:00.0Z", - "last_check_in": "1965-01-17T23:00:00.0Z" - }, - "Excepteur_b": { - "stale_timestamp": "1994-04-14T22:00:00.0Z", - "last_check_in": "1944-06-27T22:00:00.0Z", - "check_in_succeeded": false - } - }, - "display_name": null, - "insights_id": "et irure", - "culled_timestamp": "2010-12-13T00:00:00.0Z", - "stale_timestamp": "1950-11-27T00:00:00.0Z", - "mac_addresses": null, - "ansible_host": "do culpa dolor veniam ullamco", - "fqdn": null, - "satellite_id": "amet", - "created": "1954-10-15T00:00:00.0Z", - "bios_uuid": "ipsum est in dolor occaecat", - "account": "labori", - "subscription_manager_id": "sed ani" - }, - { - "org_id": "dolor", - "satellite_id": null, - "insights_id": null, - "bios_uuid": "minim labore", - "updated": "1991-03-27T23:00:00.0Z", - "subscription_manager_id": null, - "ip_addresses": null, - "per_reporter_staleness": { - "aliquip6": { - "stale_timestamp": "1993-05-22T22:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "1989-03-07T23:00:00.0Z" - } - }, - "fqdn": "ea cillum laborum", - "display_name": "reprehenderit et ipsum dolore culpa", - "id": "cillum et laborum", - "stale_timestamp": null, - "stale_warning_timestamp": null, - "facts": [ - { - "namespace": "Ut" - }, - { - "namespace": "laborum ipsum" - }, - { - "namespace": "ipsum quis occaecat do" - }, - { - "namespace": "dolore sed consequ" - }, - { - "namespace": "mollit" - }, - { - "namespace": "non reprehenderit nisi exercitation" - }, - { - "namespace": "nisi" - }, - { - "namespace": "quis deserunt commodo sit ex" - }, - { - "namespace": "reprehenderit exercitation" - }, - { - "namespace": "ullamco cupidatat tempor" - }, - { - "namespace": "pariatur" - }, - { - "namespace": "labore ma" - }, - { - "namespace": "elit" - }, - { - "namespace": "labore in culpa mollit" - }, - { - "namespace": "esse cupidatat aliquip nisi" - }, - { - "namespace": "pariatur ut est" - }, - { - "namespace": "id" - }, - { - "namespace": "non eiusmod o" - }, - { - "namespace": "ex no" - }, - { - "namespace": "aute dolor dolore sunt" - } - ], - "mac_addresses": [ - "velit officia esse", - "in e", - "", - "ad fugiat nisi elit Duis", - "labore mollit", - "reprehenderit", - "ipsum voluptate", - "fugiat sed velit quis ", - "qui nostrud est fugiat", - "ullamco", - "dolore in Ut aliquip", - "sed dolor id culpa", - "est ipsum sed Excepteur", - "id", - "cillum enim culpa", - "dolor ea", - "laboris", - "dolor fugiat", - "ullamco ip", - "dolor dolore nos" - ], - "ansible_host": null, - "account": null, - "provider_id": null, - "provider_type": null, - "culled_timestamp": null, - "reporter": "laboris q", - "created": "2003-04-02T22:00:00.0Z" - }, - { - "org_id": "voluptate dolor sit", - "display_name": null, - "per_reporter_staleness": { - "inf97": { - "last_check_in": "1982-12-23T00:00:00.0Z", - "stale_timestamp": "1987-07-05T22:00:00.0Z", - "check_in_succeeded": false - }, - "sit_a": { - "stale_timestamp": "1966-09-06T23:00:00.0Z", - "last_check_in": "1948-02-09T23:00:00.0Z", - "check_in_succeeded": true - } - }, - "insights_id": null, - "satellite_id": null, - "account": "occaecat sunt", - "fqdn": null, - "subscription_manager_id": null, - "updated": "2010-04-17T22:00:00.0Z", - "facts": [ - { - "namespace": "minim" - }, - { - "namespace": "velit labore cillum ea" - }, - { - "namespace": "sunt anim laboris" - }, - { - "namespace": "sint proident" - }, - { - "namespace": "sed nulla irure sint" - }, - { - "namespace": "repreh" - }, - { - "namespace": "commodo quis anim " - }, - { - "namespace": "consectetur Lorem reprehenderit" - }, - { - "namespace": "sunt" - }, - { - "namespace": "irure eu do" - }, - { - "namespace": "minim Lorem" - }, - { - "namespace": "reprehenderit" - }, - { - "namespace": "qui" - }, - { - "namespace": "laboris culpa non dolore sit" - }, - { - "namespace": "sunt est Excepteur" - }, - { - "namespace": "do culpa" - }, - { - "namespace": "Ut" - }, - { - "namespace": "ipsum" - }, - { - "namespace": "nulla" - }, - { - "namespace": "cillum" - } - ], - "ip_addresses": [ - "velit sed do", - "esse fugiat pariatu", - "Excepteur proident exercita", - "veniam nisi", - "non qui", - "reprehenderit sed quis", - "Excepteur qui in", - "sit Excepteur ea qui irure", - "id enim veniam deserunt tempor", - "dolore elit culpa do aliquip", - "Excepteur esse dolore id eu", - "dolore", - "sunt non in qui", - "sed", - "dolor", - "proident nostrud Duis dolor", - "par", - "dolore aute quis nulla nisi", - "eiusmod laborum in", - "ullamco proident dolore ex id" - ], - "ansible_host": null, - "stale_timestamp": "2002-07-30T22:00:00.0Z", - "created": "1967-03-04T23:00:00.0Z", - "stale_warning_timestamp": null, - "provider_type": null, - "reporter": "ipsum aliqua", - "provider_id": "velit dolore", - "bios_uuid": null, - "mac_addresses": [ - "irure non reprehenderit aute", - "occaecat", - "sit", - "dolore ad Ut moll", - "in aliqua non ea esse", - "labore", - "proident ut", - "commodo dolore pariatur ipsum ex", - "quis mollit consequat ex est", - "amet dolor eu sed Ut", - "non Lorem Duis aliqua", - "ad nisi", - "cillum eiusmod nisi", - "labore amet deserunt veniam", - "ipsu", - "sunt", - "", - "dolore elit amet", - "eu ipsum mollit ut amet", - "commodo labore fugiat" - ], - "culled_timestamp": null, - "id": "dolor culpa" - }, - { - "org_id": "esse irure reprehenderit ad", - "bios_uuid": null, - "created": "2012-03-24T23:00:00.0Z", - "satellite_id": null, - "fqdn": "irure do ullamco", - "insights_id": "cillum in laboris ex ad", - "subscription_manager_id": null, - "per_reporter_staleness": { - "adipisicing_c": { - "last_check_in": "1965-03-17T23:00:00.0Z", - "stale_timestamp": "1946-12-22T00:00:00.0Z", - "check_in_succeeded": false - }, - "etc18": { - "check_in_succeeded": false, - "last_check_in": "2015-08-11T22:00:00.0Z", - "stale_timestamp": "1964-09-05T23:00:00.0Z" - }, - "dolore_27": { - "stale_timestamp": "2002-03-24T23:00:00.0Z", - "check_in_succeeded": false, - "last_check_in": "2016-11-28T00:00:00.0Z" - }, - "nulla1d": { - "last_check_in": "1947-11-16T00:00:00.0Z", - "stale_timestamp": "2002-11-22T00:00:00.0Z", - "check_in_succeeded": false - } - }, - "id": "pariatur aute ut", - "updated": "1991-03-03T23:00:00.0Z", - "provider_type": "dolor", - "culled_timestamp": "1995-09-17T22:00:00.0Z", - "ip_addresses": [ - "quis no", - "ea ullamco deserunt ", - "proident", - "incididunt commo", - "nostrud pariatur deserunt esse", - "ullamco", - "Duis", - "consequat Excepteur in aliquip nulla", - "velit nostrud exe", - "Ut Excepteur non consectetur", - "Lorem sed minim laboris Duis", - "eiusmod ad ullamco culpa", - "sit", - "qui", - "est officia cupidatat Ut non", - "officia", - "in ut", - "reprehenderit est", - "eiusmod Duis aliquip", - "enim reprehenderit eu" - ], - "facts": [ - { - "namespace": "veniam in Ut" - }, - { - "namespace": "incididunt fugiat ipsum minim" - }, - { - "namespace": "quis" - }, - { - "namespace": "ex qui cillum" - }, - { - "namespace": "nostrud" - }, - { - "namespace": "sed velit" - }, - { - "namespace": "quis eli" - }, - { - "namespace": "ex veniam in fugiat" - }, - { - "namespace": "proident commodo officia" - }, - { - "namespace": "aliqua" - }, - { - "namespace": "minim" - }, - { - "namespace": "ipsum aliquip cupidatat" - }, - { - "namespace": "fugiat in" - }, - { - "namespace": "sint ea mollit" - }, - { - "namespace": "voluptate" - }, - { - "namespace": "esse commodo" - }, - { - "namespace": "nulla" - }, - { - "namespace": "dolor ullamco quis velit esse" - }, - { - "namespace": "nostrud" - }, - { - "namespace": "proident" - } - ], - "ansible_host": null, - "provider_id": "aliquip Ut eni", - "reporter": null, - "account": null, - "display_name": null, - "stale_warning_timestamp": "1958-08-14T23:00:00.0Z", - "stale_timestamp": null, - "mac_addresses": null - } - ], - "total": 630 -} diff --git a/cypress/support/interceptors.js b/cypress/support/interceptors.js index 173d54c45..97bdbbed2 100644 --- a/cypress/support/interceptors.js +++ b/cypress/support/interceptors.js @@ -1,38 +1,21 @@ /* eslint-disable camelcase */ import { DEFAULT_ROW_COUNT } from '@redhat-cloud-services/frontend-components-utilities'; - -// fixtures generated by prism mock server import fixtures from '../fixtures/groups.json'; import groupsSecondPage from '../fixtures/groupsSecondPage.json'; import groupDetailFixtures from '../fixtures/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C.json'; -import hostsFixtures from '../fixtures/hosts.json'; -export { hostsFixtures, groupDetailFixtures }; export const groupsInterceptors = { 'successful with some items': () => - cy - .intercept('GET', '/api/inventory/v1/groups*', { - statusCode: 200, - body: fixtures - }) - .as('getGroups'), + cy.intercept('GET', '/api/inventory/v1/groups*', fixtures).as('getGroups'), 'successful with some items second page': () => - cy - .intercept('GET', '/api/inventory/v1/groups?*page=2&perPage=50*', { - statusCode: 200, - body: groupsSecondPage - }) - .as('getGroupsSecond'), + cy.intercept('GET', '/api/inventory/v1/groups?*page=2&perPage=50*', groupsSecondPage).as('getGroupsSecond'), 'successful empty': () => cy .intercept('GET', '/api/inventory/v1/groups*', { - statusCode: 200, - body: { - count: 0, - page: 1, - per_page: DEFAULT_ROW_COUNT, - total: 0 - } + count: 0, + page: 1, + per_page: DEFAULT_ROW_COUNT, + total: 0 }) .as('getGroups'), 'failed with server error': () => { @@ -44,10 +27,11 @@ export const groupsInterceptors = { ); }, 'long responding': () => { - cy.intercept('GET', '/api/inventory/v1/groups*', { - statusCode: 200, - body: fixtures, - delay: 42000000 // milliseconds + cy.intercept('GET', '/api/inventory/v1/groups*', (req) => { + req.reply({ + body: fixtures, + delay: 42000000 // milliseconds + }); }).as('getGroups'); } }; @@ -55,71 +39,27 @@ export const groupsInterceptors = { export const groupDetailInterceptors = { successful: () => cy - .intercept( - 'GET', - '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', - { - statusCode: 200, - body: groupDetailFixtures - } - ) - .as('getGroupDetail'), - 'successful with hosts': () => - cy - .intercept( - 'GET', - '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', - { - statusCode: 200, - body: { - ...groupDetailFixtures, - results: [{ - ...groupDetailFixtures.results[0], - host_ids: ['host-1', 'host-2'] - }] - } - } - ) + .intercept('GET', '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', groupDetailFixtures) .as('getGroupDetail'), empty: () => cy - .intercept( - 'GET', - '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', - { statusCode: 404 } - ) + .intercept('GET', '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', { statusCode: 404 }) .as('getGroupDetail'), 'failed with server error': () => { Cypress.on('uncaught:exception', () => { return false; }); - cy.intercept( - 'GET', - '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', - { statusCode: 500 } - ).as('getGroupDetail'); + cy.intercept('GET', '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', { statusCode: 500 }).as( + 'getGroupDetail' + ); }, 'long responding': () => { - cy.intercept( - 'GET', - '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', - { - statusCode: 200, + cy.intercept('GET', '/api/inventory/v1/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C', (req) => { + req.reply({ body: groupDetailFixtures, delay: 42000000 // milliseconds - } - ) - .as('getGroupDetail'); - }, - 'patch successful': () => { - cy - .intercept('PATCH', '/api/inventory/v1/groups/*', { statusCode: 200 }) - .as('patchGroup'); - }, - 'delete successful': () => { - cy - .intercept('DELETE', '/api/inventory/v1/groups/*', { statusCode: 204 }) - .as('deleteGroup'); + }); + }).as('getGroupDetail'); } }; @@ -135,63 +75,3 @@ export const deleteGroupsInterceptors = { }).as('deleteGroups'); } }; - -export const hostsInterceptors = { - successful: () => { - cy.intercept('GET', '/api/inventory/v1/hosts*', { - statusCode: 200, - body: hostsFixtures - }).as('getHosts'); - }, - 'successful empty': () => { - cy.intercept('GET', '/api/inventory/v1/hosts*', { - statusCode: 200, - body: { - count: 0, - page: 1, - per_page: DEFAULT_ROW_COUNT, - total: 0, - results: [] - } - }).as('getHosts'); - }, - 'failed with server error': () => { - Cypress.on('uncaught:exception', () => { - return false; - }); - cy.intercept('GET', '/api/inventory/v1/hosts*', { statusCode: 500 }).as( - 'getHosts' - ); - } -}; - -export const systemProfileInterceptors = { - 'operating system, successful empty': () => { - cy.intercept('GET', '/api/inventory/v1/system_profile/operating_system', { - statusCode: 200, - body: { - results: [] - } - }).as('getSystemProfile'); - } -}; - -export const featureFlagsInterceptors = { - successful: () => { - cy.intercept('GET', '/feature_flags*', { - statusCode: 200, - body: { - toggles: [ - { - name: 'hbi.ui.inventory-groups', - enabled: true, - variant: { - name: 'disabled', - enabled: true - } - } - ] - } - }).as('getFeatureFlag'); - } -}; diff --git a/cypress/support/utils.js b/cypress/support/utils.js deleted file mode 100644 index 80ca08455..000000000 --- a/cypress/support/utils.js +++ /dev/null @@ -1,24 +0,0 @@ -import { ROW } from '@redhat-cloud-services/frontend-components-utilities'; - -export const ORDER_TO_URL = { - ascending: 'ASC', - descending: 'DESC' -}; - -export const selectRowN = (number) => { - cy.get(ROW).eq(number).find('.pf-c-table__check').click(); -}; - -export const checkSelectedNumber = (number, selector = '#toggle-checkbox-text') => { - if (number === 0) { - cy.get(selector).should('not.exist'); - } else { - cy.get(selector).should('have.text', `${number} selected`); - } -}; - -export const unleashDummyConfig = { - url: 'http://localhost:8002/feature_flags', - clientKey: 'abc', - appName: 'abc' -}; diff --git a/doc/props_table.md b/doc/props_table.md index dbc0e28c3..4c7c8fd76 100644 --- a/doc/props_table.md +++ b/doc/props_table.md @@ -90,9 +90,7 @@ Function called when table is refreshed. *object* -Props passed to table component. In addition it is used to pass `actionResolver` props. -That will give you access to the row data, that way you can map through each row and enable/disable kebab action depending on the value. You cannot use both `actions` and `actionResolver` props - choose one. -Example in [Patchman UI](https://github.com/RedHatInsights/patchman-ui/blob/master/src/SmartComponents/Systems/Systems.js#L191) +Props passed to table component. ## paginationProps diff --git a/package-lock.json b/package-lock.json index fa52f3ef0..59aecfd12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "insights-inventory-frontend", - "version": "1.14.8", + "version": "1.7.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "insights-inventory-frontend", - "version": "1.14.8", + "version": "1.7.2", "dependencies": { "@data-driven-forms/common": "^3.20.0", "@data-driven-forms/pf4-component-mapper": "^3.20.0", @@ -14,7 +14,7 @@ "@patternfly/react-core": "^4.265.2", "@patternfly/react-icons": "^4.93.6", "@patternfly/react-table": "^4.111.45", - "@redhat-cloud-services/frontend-components": "^3.9.33", + "@redhat-cloud-services/frontend-components": "^3.9.25", "@redhat-cloud-services/frontend-components-notifications": "^3.2.12", "@redhat-cloud-services/frontend-components-utilities": "^3.3.13", "@redhat-cloud-services/host-inventory-client": "1.0.116", @@ -3612,20 +3612,6 @@ "@octokit/openapi-types": "^16.0.0" } }, - "node_modules/@openshift/dynamic-plugin-sdk": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@openshift/dynamic-plugin-sdk/-/dynamic-plugin-sdk-2.0.1.tgz", - "integrity": "sha512-fiSPxk8ghs/aEp7UasDBhjdXrQ5/IQl+QuCB8FHz6IhAkN5mB/aQ7GcBHfW+ITK4g0eb6ydb4x2IaKP8iZeBJw==", - "dependencies": { - "lodash-es": "^4.17.21", - "semver": "^7.3.7", - "uuid": "^8.3.2", - "yup": "^0.32.11" - }, - "peerDependencies": { - "react": "^17.0.2" - } - }, "node_modules/@patternfly/patternfly": { "version": "4.224.2", "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.224.2.tgz", @@ -3856,14 +3842,14 @@ "dev": true }, "node_modules/@redhat-cloud-services/frontend-components": { - "version": "3.9.33", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-3.9.33.tgz", - "integrity": "sha512-DtqxppMpkmtyLUtRjMIUgUqYPBOztYQgucbmU4BdKCIuugaFrBwMisMxaf2tUKCdZ/fY4lvYUFJghjDEWfdbDA==", + "version": "3.9.25", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-3.9.25.tgz", + "integrity": "sha512-ynppJJrMtkgzt5/uUF9rR51HeE5WkC5kCkNzIBqAFHHMsGueGazc9y5g1nzPkl7jgBajsTg7tePCLCvKP/HEnw==", "dependencies": { "@redhat-cloud-services/frontend-components-utilities": "^3.2.25", - "@redhat-cloud-services/types": "^0.0.17", - "@scalprum/core": "^0.4.0", - "@scalprum/react-core": "^0.4.0", + "@redhat-cloud-services/types": "^0.0.15", + "@scalprum/core": "^0.2.3", + "@scalprum/react-core": "^0.2.4", "sanitize-html": "^2.7.2" }, "peerDependencies": { @@ -3873,9 +3859,9 @@ "classnames": "^2.2.5", "lodash": "^4.17.15", "prop-types": "^15.6.2", - "react": "^16.14.0 || ^17.0.0 || ^18.0.0", + "react": "^16.14.0 || ^17.0.0", "react-content-loader": "^6.2.0", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.14.0 || ^17.0.0", "react-redux": ">=7.0.0", "react-router-dom": "^5.0.0 || ^6.0.0" } @@ -4118,6 +4104,11 @@ "react-router-dom": "^5.0.0 || ^6.0.0" } }, + "node_modules/@redhat-cloud-services/frontend-components-utilities/node_modules/@redhat-cloud-services/types": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.17.tgz", + "integrity": "sha512-3V9mmarS3jD5fBksMwh+XCEAMUIzqSOxDkH6OcIVu6w/gaBBOWHh34Jwn4CxKlu+WStxVV/rxm3oFGpsWqljvg==" + }, "node_modules/@redhat-cloud-services/frontend-components-utilities/node_modules/axios": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", @@ -4163,25 +4154,21 @@ } }, "node_modules/@redhat-cloud-services/types": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.17.tgz", - "integrity": "sha512-3V9mmarS3jD5fBksMwh+XCEAMUIzqSOxDkH6OcIVu6w/gaBBOWHh34Jwn4CxKlu+WStxVV/rxm3oFGpsWqljvg==" + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.15.tgz", + "integrity": "sha512-1aqJcgQZq4uih+LxRpVJQblt2x4o/hlrqSZMYFhWyTLgnVNhJ8Y7B5pwoVjpA5PCE1fBNahrydVwugEKMsDDtg==" }, "node_modules/@scalprum/core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.4.1.tgz", - "integrity": "sha512-Ff8G2Mhc6ORPx+5C/B6vYYyGL2mBmQ8jR1I0yhgmYClzZU4gzQalZrSIwBDozGCoYmdKggF+hPCxojFwgE227g==", - "dependencies": { - "@openshift/dynamic-plugin-sdk": "^2.0.1" - } + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.2.3.tgz", + "integrity": "sha512-bL7YjXWSgtAw44ha+goEF/cCWUu1BELB0qo4Y8hlfmn0+FMnoIHcY0gD1OOotz7Oy74r5+DRxi5Wra40DTG8Qg==" }, "node_modules/@scalprum/react-core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@scalprum/react-core/-/react-core-0.4.1.tgz", - "integrity": "sha512-R5gtrnqbeR6qRDUddZAtJUDUYOU+HjMbTROAYP6ryFzFnwbDBPY1DtNx4n8458yaZBRRiPYfkJEWvWzui1D0hw==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@scalprum/react-core/-/react-core-0.2.8.tgz", + "integrity": "sha512-+qGfiA6FkXAx4x53fHmv7Q3oZcEQK0NChgaVeKGaZfG+LSNa1ozgkd4oSWueAMG3XV3St0QbAxzAtRQNFRyqNQ==", "dependencies": { - "@openshift/dynamic-plugin-sdk": "^2.0.1", - "@scalprum/core": "^0.4.1", + "@scalprum/core": "^0.2.3", "lodash": "^4.17.0" }, "peerDependencies": { @@ -5735,11 +5722,6 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, - "node_modules/@types/lodash": { - "version": "4.14.191", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", - "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" - }, "node_modules/@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -15977,11 +15959,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -16871,11 +16848,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "node_modules/nanoclone": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", - "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" - }, "node_modules/nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -21555,11 +21527,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" - }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -24668,11 +24635,6 @@ "node": ">=0.6" } }, - "node_modules/toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" - }, "node_modules/totalist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", @@ -26350,23 +26312,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yup": { - "version": "0.32.11", - "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", - "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", - "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/lodash": "^4.14.175", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "nanoclone": "^0.2.1", - "property-expr": "^2.0.4", - "toposort": "^2.0.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", @@ -28967,17 +28912,6 @@ "@octokit/openapi-types": "^16.0.0" } }, - "@openshift/dynamic-plugin-sdk": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@openshift/dynamic-plugin-sdk/-/dynamic-plugin-sdk-2.0.1.tgz", - "integrity": "sha512-fiSPxk8ghs/aEp7UasDBhjdXrQ5/IQl+QuCB8FHz6IhAkN5mB/aQ7GcBHfW+ITK4g0eb6ydb4x2IaKP8iZeBJw==", - "requires": { - "lodash-es": "^4.17.21", - "semver": "^7.3.7", - "uuid": "^8.3.2", - "yup": "^0.32.11" - } - }, "@patternfly/patternfly": { "version": "4.224.2", "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.224.2.tgz", @@ -29126,14 +29060,14 @@ "dev": true }, "@redhat-cloud-services/frontend-components": { - "version": "3.9.33", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-3.9.33.tgz", - "integrity": "sha512-DtqxppMpkmtyLUtRjMIUgUqYPBOztYQgucbmU4BdKCIuugaFrBwMisMxaf2tUKCdZ/fY4lvYUFJghjDEWfdbDA==", + "version": "3.9.25", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-3.9.25.tgz", + "integrity": "sha512-ynppJJrMtkgzt5/uUF9rR51HeE5WkC5kCkNzIBqAFHHMsGueGazc9y5g1nzPkl7jgBajsTg7tePCLCvKP/HEnw==", "requires": { "@redhat-cloud-services/frontend-components-utilities": "^3.2.25", - "@redhat-cloud-services/types": "^0.0.17", - "@scalprum/core": "^0.4.0", - "@scalprum/react-core": "^0.4.0", + "@redhat-cloud-services/types": "^0.0.15", + "@scalprum/core": "^0.2.3", + "@scalprum/react-core": "^0.2.4", "sanitize-html": "^2.7.2" } }, @@ -29319,6 +29253,11 @@ "react-content-loader": "^6.2.0" }, "dependencies": { + "@redhat-cloud-services/types": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.17.tgz", + "integrity": "sha512-3V9mmarS3jD5fBksMwh+XCEAMUIzqSOxDkH6OcIVu6w/gaBBOWHh34Jwn4CxKlu+WStxVV/rxm3oFGpsWqljvg==" + }, "axios": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", @@ -29363,25 +29302,21 @@ } }, "@redhat-cloud-services/types": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.17.tgz", - "integrity": "sha512-3V9mmarS3jD5fBksMwh+XCEAMUIzqSOxDkH6OcIVu6w/gaBBOWHh34Jwn4CxKlu+WStxVV/rxm3oFGpsWqljvg==" + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/types/-/types-0.0.15.tgz", + "integrity": "sha512-1aqJcgQZq4uih+LxRpVJQblt2x4o/hlrqSZMYFhWyTLgnVNhJ8Y7B5pwoVjpA5PCE1fBNahrydVwugEKMsDDtg==" }, "@scalprum/core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.4.1.tgz", - "integrity": "sha512-Ff8G2Mhc6ORPx+5C/B6vYYyGL2mBmQ8jR1I0yhgmYClzZU4gzQalZrSIwBDozGCoYmdKggF+hPCxojFwgE227g==", - "requires": { - "@openshift/dynamic-plugin-sdk": "^2.0.1" - } + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.2.3.tgz", + "integrity": "sha512-bL7YjXWSgtAw44ha+goEF/cCWUu1BELB0qo4Y8hlfmn0+FMnoIHcY0gD1OOotz7Oy74r5+DRxi5Wra40DTG8Qg==" }, "@scalprum/react-core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@scalprum/react-core/-/react-core-0.4.1.tgz", - "integrity": "sha512-R5gtrnqbeR6qRDUddZAtJUDUYOU+HjMbTROAYP6ryFzFnwbDBPY1DtNx4n8458yaZBRRiPYfkJEWvWzui1D0hw==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@scalprum/react-core/-/react-core-0.2.8.tgz", + "integrity": "sha512-+qGfiA6FkXAx4x53fHmv7Q3oZcEQK0NChgaVeKGaZfG+LSNa1ozgkd4oSWueAMG3XV3St0QbAxzAtRQNFRyqNQ==", "requires": { - "@openshift/dynamic-plugin-sdk": "^2.0.1", - "@scalprum/core": "^0.4.1", + "@scalprum/core": "^0.2.3", "lodash": "^4.17.0" } }, @@ -30648,11 +30583,6 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, - "@types/lodash": { - "version": "4.14.191", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", - "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" - }, "@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -38420,11 +38350,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -39102,11 +39027,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nanoclone": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", - "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" - }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -42512,11 +42432,6 @@ } } }, - "property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" - }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -44910,11 +44825,6 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, - "toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" - }, "totalist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", @@ -46164,20 +46074,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" }, - "yup": { - "version": "0.32.11", - "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", - "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", - "requires": { - "@babel/runtime": "^7.15.4", - "@types/lodash": "^4.14.175", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "nanoclone": "^0.2.1", - "property-expr": "^2.0.4", - "toposort": "^2.0.2" - } - }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index 5de25b6c3..8ed65fee6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "insights-inventory-frontend", - "version": "1.14.8", + "version": "1.7.2", "private": false, "engines": { "node": ">=15.0.0", @@ -13,7 +13,7 @@ "@patternfly/react-core": "^4.265.2", "@patternfly/react-icons": "^4.93.6", "@patternfly/react-table": "^4.111.45", - "@redhat-cloud-services/frontend-components": "^3.9.33", + "@redhat-cloud-services/frontend-components": "^3.9.25", "@redhat-cloud-services/frontend-components-notifications": "^3.2.12", "@redhat-cloud-services/frontend-components-utilities": "^3.3.13", "@redhat-cloud-services/host-inventory-client": "1.0.116", diff --git a/src/Utilities/ChromeLoader.js b/src/Utilities/ChromeLoader.js deleted file mode 100644 index 246cf5bc2..000000000 --- a/src/Utilities/ChromeLoader.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; - -const ChromeLoader = ({ children }) => { - const chrome = useChrome(); - - return <> - {React.Children.map(children, child => { - if (React.isValidElement(child)) { - return React.cloneElement(child, { chrome }); - } - - return child; - })} - ; -}; - -ChromeLoader.propTypes = { - children: PropTypes.any -}; - -export default ChromeLoader; diff --git a/src/Utilities/DeleteModal.js b/src/Utilities/DeleteModal.js index 318f154d5..fb37bfc20 100644 --- a/src/Utilities/DeleteModal.js +++ b/src/Utilities/DeleteModal.js @@ -27,7 +27,7 @@ const DeleteModal = ({ handleModalToggle, isModalOpen, currentSytems, onConfirm return handleModalToggle(false)} diff --git a/src/Utilities/__snapshots__/DeleteModal.test.js.snap b/src/Utilities/__snapshots__/DeleteModal.test.js.snap index 6c38f8f4e..bf491e42a 100644 --- a/src/Utilities/__snapshots__/DeleteModal.test.js.snap +++ b/src/Utilities/__snapshots__/DeleteModal.test.js.snap @@ -7,7 +7,7 @@ exports[`EntityTable DOM should render correctly 1`] = ` aria-describedby="" aria-label="" aria-labelledby="" - className="ins-c-inventory__table--remove sentry-mask data-hj-suppress" + className="ins-c-inventory__table--remove" hasNoBodyWrapper={false} isOpen={false} onClose={[Function]} @@ -103,7 +103,7 @@ exports[`EntityTable DOM should render correctly with multiple systems - count 1 aria-describedby="" aria-label="" aria-labelledby="" - className="ins-c-inventory__table--remove sentry-mask data-hj-suppress" + className="ins-c-inventory__table--remove" hasNoBodyWrapper={false} isOpen={true} onClose={[Function]} @@ -200,7 +200,7 @@ exports[`EntityTable DOM should render correctly with multiple systems - count 2 aria-describedby="" aria-label="" aria-labelledby="" - className="ins-c-inventory__table--remove sentry-mask data-hj-suppress" + className="ins-c-inventory__table--remove" hasNoBodyWrapper={false} isOpen={true} onClose={[Function]} @@ -297,7 +297,7 @@ exports[`EntityTable DOM should render correctly with one system 1`] = ` aria-describedby="" aria-label="" aria-labelledby="" - className="ins-c-inventory__table--remove sentry-mask data-hj-suppress" + className="ins-c-inventory__table--remove" hasNoBodyWrapper={false} isOpen={true} onClose={[Function]} @@ -394,7 +394,7 @@ exports[`EntityTable DOM should render correctly with one system 2`] = ` aria-describedby="" aria-label="" aria-labelledby="" - className="ins-c-inventory__table--remove sentry-mask data-hj-suppress" + className="ins-c-inventory__table--remove" hasNoBodyWrapper={false} isOpen={true} onClose={[Function]} diff --git a/src/Utilities/constants.js b/src/Utilities/constants.js index aeec8cfe0..ed6147e0f 100644 --- a/src/Utilities/constants.js +++ b/src/Utilities/constants.js @@ -10,14 +10,6 @@ export const REGISTERED_CHIP = 'registered_with'; export const OS_CHIP = 'operating_system'; export const RHCD_FILTER_KEY = 'rhc_client_id'; export const UPDATE_METHOD_KEY = 'system_update_method'; -export const LAST_SEEN_CHIP = 'last_seen'; -export const HOST_GROUP_CHIP = 'host_group'; - -export function subtractDate(days) { - const date = new Date(); - date.setDate(date.getDate() - days); - return date.toISOString(); -} export const staleness = [ { label: 'Fresh', value: 'fresh' }, @@ -25,34 +17,6 @@ export const staleness = [ { label: 'Stale warning', value: 'stale_warning' }, { label: 'Unknown', value: 'unknown' } ]; - -export const currentDate = new Date().toISOString(); -export const lastSeenItems = [ - { - value: { updatedStart: subtractDate(1), updatedEnd: currentDate, mark: 'last24' }, - label: 'Within the last 24 hours' }, - { - value: { updatedEnd: subtractDate(1), mark: '24more' }, - label: 'More than 1 day ago' - - }, - { - value: { updatedEnd: subtractDate(7), mark: '7more' }, - label: 'More than 7 days ago' - }, - { - value: { updatedEnd: subtractDate(15), mark: '15more' }, - label: 'More than 15 days ago' - }, - { - value: { updatedEnd: subtractDate(30), mark: '30more' }, - label: 'More than 30 days ago' - }, - { - value: { mark: 'custom' }, - label: 'Custom' - } -]; export const registered = [ { label: 'insights-client', value: 'puptoo', idName: 'Insights id', idValue: 'insights_id' }, { label: 'subscription-manager', value: 'rhsm-conduit', @@ -117,8 +81,7 @@ export function reduceFilters(filters = []) { }; } - const foundKey = ['staleFilter', 'registeredWithFilter', 'osFilter', 'rhcdFilter', 'updateMethodFilter', - 'lastSeenFilter', ''] + const foundKey = ['staleFilter', 'registeredWithFilter', 'osFilter', 'rhcdFilter', 'updateMethodFilter', ''] .find(item => Object.keys(oneFilter).includes(item)); return { @@ -142,16 +105,7 @@ export const reloadWrapper = (event, callback) => { export const isEmpty = (check) => !check || check?.length === 0; -export const generateFilter = (status, - source, - tagsFilter, - filterbyName, - operatingSystem, - rhcdFilter, - updateMethodFilter, - hostGroup, - lastSeenFilter -) => ([ +export const generateFilter = (status, source, tagsFilter, filterbyName, operatingSystem, rhcdFilter, updateMethodFilter) => ([ !isEmpty(status) && { staleFilter: Array.isArray(status) ? status : [status] }, @@ -177,15 +131,8 @@ export const generateFilter = (status, !isEmpty(rhcdFilter) && { rhcdFilter: Array.isArray(rhcdFilter) ? rhcdFilter : [rhcdFilter] }, - !isEmpty(lastSeenFilter) && { - lastSeenFilter: Array.isArray(lastSeenFilter) - ? lastSeenItems.filter((item)=> item.value.mark === lastSeenFilter[0])[0].value : [lastSeenFilter] - }, !isEmpty(updateMethodFilter) && { updateMethodFilter: Array.isArray(updateMethodFilter) ? updateMethodFilter : [updateMethodFilter] - }, - !isEmpty(hostGroup) && { - hostGroupFilter: Array.isArray(hostGroup) ? hostGroup : [hostGroup] } ].filter(Boolean)); diff --git a/src/api/api.js b/src/api/api.js index 5a0eeae55..494259848 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -98,9 +98,7 @@ export const filtersReducer = (acc, filter = {}) => ({ ...'registeredWithFilter' in filter && { registeredWithFilter: filter.registeredWithFilter }, ...'osFilter' in filter && { osFilter: filter.osFilter }, ...'rhcdFilter' in filter && { rhcdFilter: filter.rhcdFilter }, - ...'lastSeenFilter' in filter && { lastSeenFilter: filter.lastSeenFilter }, - ...'updateMethodFilter' in filter && { updateMethodFilter: filter.updateMethodFilter }, - ...'groupHostFilter' in filter && { groupHostFilter: filter.groupHostFilter } + ...'updateMethodFilter' in filter && { updateMethodFilter: filter.updateMethodFilter } }); export async function getEntities(items, { @@ -111,10 +109,9 @@ export async function getEntities(items, { page, orderBy, orderDirection, - fields = { system_profile: ['operating_system', /* needed by inventory groups */ 'system_update_method'] }, + fields = { system_profile: ['operating_system'] }, ...options }, showTags) { - if (hasItems && items?.length > 0) { let data = await hosts.apiHostGetHostById( items, @@ -183,8 +180,8 @@ export async function getEntities(items, { orderDirection, filters.staleFilter, [ - ...constructTags(filters?.tagFilters), - ...options?.globalFilter?.tags || [] + ...constructTags(filters.tagFilters), + ...options.tags || [] ], filters?.registeredWithFilter, undefined, @@ -192,12 +189,9 @@ export async function getEntities(items, { { cancelToken: controller && controller.token, query: { - ...(options?.globalFilter?.filter && generateFilter(options.globalFilter.filter)), ...(options.filter && Object.keys(options.filter).length && generateFilter(options.filter)), ...(calculateSystemProfile(filters)), - ...(fields && Object.keys(fields).length && generateFilter(fields, 'fields')), - ...filters?.lastSeenFilter?.updatedStart && { updated_start: filters.lastSeenFilter.updatedStart }, - ...filters?.lastSeenFilter?.updatedEnd && { updated_end: filters.lastSeenFilter.updatedEnd } + ...(fields && Object.keys(fields).length && generateFilter(fields, 'fields')) } } ) diff --git a/src/components/GeneralInfo/SystemCard/SystemCard.js b/src/components/GeneralInfo/SystemCard/SystemCard.js index 504faf2b5..76a589e1c 100644 --- a/src/components/GeneralInfo/SystemCard/SystemCard.js +++ b/src/components/GeneralInfo/SystemCard/SystemCard.js @@ -170,7 +170,6 @@ class SystemCardCore extends Component { inputOuiaId="input-edit-display-name" onCancel={ this.onCancel } onSubmit={ this.onSubmit(setDisplayName, entity && entity.display_name) } - className ='sentry-mask data-hj-suppress' /> ); diff --git a/src/components/GroupSystems/GroupSystems.cy.js b/src/components/GroupSystems/GroupSystems.cy.js deleted file mode 100644 index a9286371a..000000000 --- a/src/components/GroupSystems/GroupSystems.cy.js +++ /dev/null @@ -1,365 +0,0 @@ -import { mount } from '@cypress/react'; -import { - changePagination, - checkEmptyState, - checkPaginationTotal, - checkPaginationValues, - checkTableHeaders, - CHIP, - CHIP_GROUP, - DROPDOWN_ITEM, - DROPDOWN_TOGGLE, - hasChip, - MODAL, - PAGINATION_VALUES, - SORTING_ORDERS, - TEXT_INPUT, - TOOLBAR, - TOOLBAR_FILTER -} from '@redhat-cloud-services/frontend-components-utilities'; -import FlagProvider from '@unleash/proxy-client-react'; -import React from 'react'; -import { Provider } from 'react-redux'; -import { MemoryRouter } from 'react-router-dom'; -import { - featureFlagsInterceptors, - groupsInterceptors, - hostsInterceptors, - systemProfileInterceptors -} from '../../../cypress/support/interceptors'; -import { getStore } from '../../store'; -import GroupSystems from './GroupSystems'; -import fixtures from '../../../cypress/fixtures/hosts.json'; -import { - checkSelectedNumber as checkSelectedNumber_, - ORDER_TO_URL, - selectRowN, - unleashDummyConfig -} from '../../../cypress/support/utils'; -import _ from 'lodash'; - -const GROUP_NAME = 'foobar'; -const ROOT = 'div[id="group-systems-table"]'; -const TABLE_HEADERS = ['Name', 'Tags', 'OS', 'Update method', 'Last seen']; -const SORTABLE_HEADERS = ['Name', 'OS', 'Last seen']; -const DEFAULT_ROW_COUNT = 50; - -const checkSelectedNumber = (number) => - checkSelectedNumber_(number, '#bulk-select-systems-toggle-checkbox-text'); - -const mountTable = () => - mount( - - - - - - - - ); - -before(() => { - cy.window().then( - (window) => - (window.insights = { - chrome: { - isProd: false, - auth: { - getUser: () => { - return Promise.resolve({}); - } - } - } - }) - ); -}); - -describe('renders correctly', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - it('the root container is rendered', () => { - cy.get(ROOT); - }); - - it('renders toolbar', () => { - cy.get(TOOLBAR).should('have.length', 1); - }); - - it('renders table header', () => { - checkTableHeaders(TABLE_HEADERS); - }); -}); - -describe('defaults', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.wait('@getHosts'); - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - it(`pagination is set to ${DEFAULT_ROW_COUNT}`, () => { - cy.get('.pf-c-options-menu__toggle-text') - .find('b') - .eq(0) - .should('have.text', `1 - ${DEFAULT_ROW_COUNT}`); - }); - - it('name filter is a default filter', () => { - cy.get(TOOLBAR_FILTER).find(TEXT_INPUT).should('exist'); - }); -}); - -describe('pagination', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.wait('@getHosts'); - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - it('shows correct total number of groups', () => { - checkPaginationTotal(fixtures.total); - }); - - it('values are expected ones', () => { - checkPaginationValues(PAGINATION_VALUES); - }); - - it('can change page limit', () => { - PAGINATION_VALUES.forEach((el) => { - changePagination(el).then(() => { - cy.wait('@getHosts') - .its('request.url') - .should('include', `per_page=${el}`); - }); - }); - }); - - it('can change page', () => { - cy.get('button[data-action=next]').eq(0).click(); // click "next page" button - cy.wait('@getHosts').its('request.url').should('include', `page=2`); - }); -}); - -describe('sorting', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.wait('@getHosts'); - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - const checkSorting = (label, order, dataField) => { - // get appropriate locators - const header = `th[data-label="${label}"]`; - if (order === 'ascending') { - cy.get(header).find('button').click(); - } else { - cy.get(header).find('button').click(); - cy.wait('@getHosts'); // TODO: implement debounce for sorting feature - cy.get(header).find('button').click(); - } - - cy.wait('@getHosts') - .its('request.url') - .should('include', `order_how=${ORDER_TO_URL[order]}`) - .and('include', `order_by=${dataField}`); - }; - - _.zip( - ['display_name', 'operating_system', 'updated'], - SORTABLE_HEADERS - ).forEach(([category, label]) => { - SORTING_ORDERS.forEach((order) => { - it(`${order} by ${label}`, () => { - checkSorting(label, order, category); - }); - }); - }); -}); - -describe('filtering', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.wait('@getHosts'); - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - const applyNameFilter = () => - cy.get('.ins-c-primary-toolbar__filter').find('input').type('lorem'); - it('renders filter chip', () => { - applyNameFilter(); - hasChip('Display name', 'lorem'); - cy.wait('@getHosts'); - }); - - it('sends correct request', () => { - applyNameFilter(); - cy.wait('@getHosts') - .its('request.url') - .should('include', 'hostname_or_id=lorem'); - }); - - it('can remove the chip or reset filters', () => { - applyNameFilter(); - cy.wait('@getHosts') - .its('request.url') - .should('contain', 'hostname_or_id=lorem'); - cy.get(CHIP_GROUP) - .find(CHIP) - .ouiaId('close', 'button') - .each(() => { - cy.get(CHIP_GROUP).find(CHIP).ouiaId('close', 'button'); - }); - cy.get('button').contains('Reset filters').click(); - cy.wait('@getHosts') - .its('request.url') - .should('not.contain', 'hostname_or_id'); - - cy.get(CHIP_GROUP).should('not.exist'); - - cy.wait('@getHosts'); // TODO: reset filters shouldn't trigger this second extra call - }); - - it('should not contain group filter', () => { - cy.get('button[data-ouia-component-id="ConditionalFilter"]').click(); - cy.get(DROPDOWN_ITEM).should('not.contain', 'Group'); - }); - // TODO: add more filter cases -}); - -describe('selection and bulk selection', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.wait('@getHosts'); - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - it('can select and deselect systems', () => { - const middleRow = Math.ceil(DEFAULT_ROW_COUNT / 4); - selectRowN(middleRow); - checkSelectedNumber(1); - selectRowN(Math.ceil(middleRow / 2)); - checkSelectedNumber(2); - selectRowN(middleRow); - checkSelectedNumber(1); - }); - - /* it('can select all in dropdown toggle', () => { - cy.get(DROPDOWN_TOGGLE).eq(0).click(); // open selection dropdown - cy.get('.pf-c-dropdown__menu > li').eq(2).click(); - checkSelectedNumber(fixtures.total); - }); */ - - /* it('can select all by clicking checkbox', () => { - cy.get('#toggle-checkbox').eq(0).click(); - checkSelectedNumber(fixtures.total); - cy.get('#toggle-checkbox').eq(0).click(); - checkSelectedNumber(0); - }); */ - - it('can select page in dropdown toggle', () => { - cy.get(DROPDOWN_TOGGLE).eq(0).click(); // open selection dropdown - cy.get('.pf-c-dropdown__menu > li').eq(1).click(); - checkSelectedNumber(fixtures.count); - }); - - it('can select page by clicking checkbox', () => { - cy.get('#bulk-select-systems-toggle-checkbox').eq(0).click(); - checkSelectedNumber(fixtures.count); - cy.get('#bulk-select-systems-toggle-checkbox').eq(0).click(); - checkSelectedNumber(0); - }); - - it('can select none', () => { - selectRowN(1); - cy.get(DROPDOWN_TOGGLE).eq(0).click(); // open selection dropdown - cy.get('.pf-c-dropdown__menu > li').eq(1).click(); - checkSelectedNumber(0); - }); -}); - -describe('actions', () => { - beforeEach(() => { - cy.intercept('*', { statusCode: 200 }); - hostsInterceptors.successful(); - featureFlagsInterceptors.successful(); // make Groups col available - - mountTable(); - - cy.wait('@getHosts'); - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - }); - - it('can open systems add modal', () => { - cy.get('button').contains('Add systems').click(); - cy.get(MODAL).find('h1').contains('Add systems'); - - cy.wait('@getHosts'); - }); -}); - -describe('edge cases', () => { - it('no groups match', () => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors['successful empty'](); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - groupsInterceptors['successful with some items'](); - mountTable(); - - cy.wait('@getHosts'); - - checkEmptyState('No matching systems found', true); - checkPaginationTotal(0); - }); - - it('failed request', () => { - cy.intercept('*', { statusCode: 200, body: { results: [] } }); - hostsInterceptors['failed with server error'](); - featureFlagsInterceptors.successful(); - systemProfileInterceptors['operating system, successful empty'](); - mountTable(); - - cy.wait('@getHosts'); - cy.get('.pf-c-empty-state').find('h4').contains('Something went wrong'); - }); -}); diff --git a/src/components/GroupSystems/GroupSystems.js b/src/components/GroupSystems/GroupSystems.js deleted file mode 100644 index 83c5ac840..000000000 --- a/src/components/GroupSystems/GroupSystems.js +++ /dev/null @@ -1,164 +0,0 @@ -import { Button } from '@patternfly/react-core'; -import { fitContent, TableVariant } from '@patternfly/react-table'; -import difference from 'lodash/difference'; -import map from 'lodash/map'; -import PropTypes from 'prop-types'; -import React, { useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { clearFilters, selectEntity } from '../../store/inventory-actions'; -import AddSystemsToGroupModal from '../InventoryGroups/Modals/AddSystemsToGroupModal'; -import InventoryTable from '../InventoryTable/InventoryTable'; -import { Link } from 'react-router-dom'; - -export const bulkSelectConfig = (dispatch, selectedNumber, noneSelected, pageSelected, rowsNumber) => ({ - count: selectedNumber, - id: 'bulk-select-systems', - items: [ - { - title: 'Select none (0)', - onClick: () => dispatch(selectEntity(-1, false)), - props: { isDisabled: noneSelected } - }, - { - title: `${pageSelected ? 'Deselect' : 'Select'} page (${ - rowsNumber - } items)`, - onClick: () => dispatch(selectEntity(0, !pageSelected)) - } - // TODO: Implement "select all" - ], - onSelect: (value) => { - dispatch(selectEntity(0, value)); - }, - checked: selectedNumber > 0 && pageSelected // TODO: support partial selection (dash sign) in FEC BulkSelect -}); - -export const prepareColumns = (initialColumns, hideGroupColumn) => { - // hides the "groups" column - const columns = hideGroupColumn ? initialColumns.filter(({ key }) => key !== 'groups') : initialColumns; - - // additionally insert the "update method" column - columns.splice(columns.length - 2 /* must be the 3rd col from the end */, 0, { - key: 'update_method', - title: 'Update method', - sortKey: 'update_method', - transforms: [fitContent], - renderFunc: (value, hostId, systemData) => - systemData?.system_profile?.system_update_method || 'N/A', - props: { - // TODO: remove isStatic when the sorting is supported by API - isStatic: true, - width: 10 - } - }); - - columns[columns.findIndex(({ key }) => key === 'display_name')].renderFunc = - (value, hostId) => ( -
- - {value} - -
- ); - - // map columns to the speicifc order - return [ - 'display_name', - 'groups', - 'tags', - 'system_profile', - 'update_method', - 'updated' - ].map((colKey) => columns.find(({ key }) => key === colKey)) - .filter(Boolean); // eliminate possible undefined's -}; - -const GroupSystems = ({ groupName, groupId }) => { - const dispatch = useDispatch(); - - const selected = useSelector( - (state) => state?.entities?.selected || new Map() - ); - const rows = useSelector(({ entities }) => entities?.rows || []); - - const noneSelected = selected.size === 0; - const displayedIds = map(rows, 'id'); - const pageSelected = - difference(displayedIds, [...selected.keys()]).length === 0; - - const [isModalOpen, setIsModalOpen] = useState(false); - - const resetTable = () => { - dispatch(clearFilters()); - dispatch(selectEntity(-1, false)); - }; - - useEffect(() => { - return () => { - resetTable(); - }; - }, []); - - return ( -
- { - isModalOpen && { - resetTable(); - setIsModalOpen(value); - } - } - groupId={groupId} - groupName={groupName} - /> - } - { - !isModalOpen && - prepareColumns(columns, true)} - hideFilters={{ hostGroupFilter: true }} - getEntities={async (items, config, showTags, defaultGetEntities) => - await defaultGetEntities( - items, - // filter systems by the group name - { - ...config, - filters: { - ...config.filters, - groupName: [groupName] // TODO: the param is not yet supported by `apiHostGetHostList` - } - }, - showTags - ) - } - tableProps={{ - isStickyHeader: true, - variant: TableVariant.compact, - canSelectAll: false - }} - bulkSelect={bulkSelectConfig(dispatch, selected.size, noneSelected, pageSelected, rows.length)} - showTags - > - - - } -
- ); -}; - -GroupSystems.propTypes = { - groupName: PropTypes.string.isRequired, - groupId: PropTypes.string.isRequired -}; - -export default GroupSystems; diff --git a/src/components/GroupSystems/index.js b/src/components/GroupSystems/index.js deleted file mode 100644 index 9c7177334..000000000 --- a/src/components/GroupSystems/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import { EmptyState, EmptyStateBody, Spinner } from '@patternfly/react-core'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { useSelector } from 'react-redux'; -import NoSystemsEmptyState from '../InventoryGroupDetail/NoSystemsEmptyState'; -import GroupSystems from './GroupSystems'; - -const GroupSystemsWrapper = ({ groupName, groupId }) => { - const { uninitialized, loading, data } = useSelector((state) => state.groupDetail); - const hosts = data?.results?.[0]?.host_ids /* can be null */ || []; - - return uninitialized || loading ? ( - - - - - - ) : hosts.length > 0 ? ( - - ) : - ; -}; - -GroupSystemsWrapper.propTypes = { - groupName: PropTypes.string.isRequired, - groupId: PropTypes.string.isRequired -}; - -export default GroupSystemsWrapper; -export { GroupSystems }; diff --git a/src/components/GroupsTable/GroupsTable.cy.js b/src/components/GroupsTable/GroupsTable.cy.js index 701461d35..f13df86b1 100644 --- a/src/components/GroupsTable/GroupsTable.cy.js +++ b/src/components/GroupsTable/GroupsTable.cy.js @@ -26,14 +26,30 @@ import { Provider } from 'react-redux'; import { MemoryRouter } from 'react-router-dom'; import fixtures from '../../../cypress/fixtures/groups.json'; import { groupsInterceptors as interceptors } from '../../../cypress/support/interceptors'; -import { checkSelectedNumber, ORDER_TO_URL, selectRowN } from '../../../cypress/support/utils'; import { getStore } from '../../store'; import GroupsTable from './GroupsTable'; +const ORDER_TO_URL = { + ascending: 'ASC', + descending: 'DESC' +}; + const DEFAULT_ROW_COUNT = 50; const TABLE_HEADERS = ['Name', 'Total systems', 'Last modified']; const ROOT = 'div[id="groups-table"]'; +export const checkSelectedNumber = (number) => { + if (number === 0) { + cy.get('#toggle-checkbox-text').should('not.exist'); + } else { + cy.get('#toggle-checkbox-text').should('have.text', `${number} selected`); + } +}; + +export const selectRowN = (number) => { + cy.get(ROW).eq(number).find('.pf-c-table__check').click(); +}; + const mountTable = () => mount( @@ -137,7 +153,7 @@ describe('sorting', () => { interceptors['successful with some items'](); mountTable(); - cy.wait('@getGroups'); // first initial request + cy.wait('@getGroups'); // first initial call }); const checkSorting = (label, order, dataField) => { @@ -171,7 +187,7 @@ describe('filtering', () => { interceptors['successful with some items'](); mountTable(); - cy.wait('@getGroups'); // first initial request + cy.wait('@getGroups'); // first initial call }); const applyNameFilter = () => @@ -185,13 +201,16 @@ describe('filtering', () => { }); it('sends correct request', () => { - applyNameFilter(); - cy.wait('@getGroups').its('request.url').should('include', 'hostname_or_id=lorem'); + applyNameFilter().then(() => { + cy.wait('@getGroups') + .its('request.url') + .should('include', 'hostname_or_id=lorem'); + }); }); it('can remove the chip or reset filters', () => { applyNameFilter(); - cy.wait('@getGroups').its('request.url').should('contain', 'hostname_or_id=lorem'); + cy.wait('@getGroups'); cy.get(CHIP_GROUP) .find(CHIP) .ouiaId('close', 'button') @@ -199,7 +218,7 @@ describe('filtering', () => { cy.get(CHIP_GROUP).find(CHIP).ouiaId('close', 'button'); }); cy.get('button').contains('Reset filters').click(); - cy.wait('@getGroups').its('request.url').should('not.contain', 'hostname_or_id'); + cy.wait('@getGroups'); cy.get(CHIP_GROUP).should('not.exist'); }); diff --git a/src/components/InventoryDetail/TopBar.js b/src/components/InventoryDetail/TopBar.js index 25496197b..408deaf61 100644 --- a/src/components/InventoryDetail/TopBar.js +++ b/src/components/InventoryDetail/TopBar.js @@ -135,7 +135,6 @@ const TopBar = ({ } { isModalOpen && ( setIsModalOpen(!isModalOpen)} isModalOpen={isModalOpen} currentSytems={entity} diff --git a/src/components/InventoryGroupDetail/GroupDetailHeader.js b/src/components/InventoryGroupDetail/GroupDetailHeader.js index ec8886392..9ef4e4830 100644 --- a/src/components/InventoryGroupDetail/GroupDetailHeader.js +++ b/src/components/InventoryGroupDetail/GroupDetailHeader.js @@ -1,108 +1,33 @@ -import { - Breadcrumb, - BreadcrumbItem, - Dropdown, - DropdownItem, - DropdownToggle, - Flex, - FlexItem, - Skeleton -} from '@patternfly/react-core'; +import { Breadcrumb, BreadcrumbItem, Skeleton } from '@patternfly/react-core'; import { PageHeader, PageHeaderTitle } from '@redhat-cloud-services/frontend-components'; -import React, { useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { Link, useHistory } from 'react-router-dom'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { Link } from 'react-router-dom'; import { routes } from '../../Routes'; import PropTypes from 'prop-types'; -import DeleteGroupModal from '../InventoryGroups/Modals/DeleteGroupModal'; -import RenameGroupModal from '../InventoryGroups/Modals/RenameGroupModal'; -import { fetchGroupDetail } from '../../store/inventory-actions'; const GroupDetailHeader = ({ groupId }) => { - const dispatch = useDispatch(); - const { uninitialized, loading, data } = useSelector( - (state) => state.groupDetail - ); - - const [dropdownOpen, setDropdownOpen] = useState(false); - const [renameModalOpen, setRenameModalOpen] = useState(false); - const [deleteModalOpen, setDeleteModalOpen] = useState(false); + const { uninitialized, loading, data } = useSelector((state) => state.groupDetail); - const name = data?.results?.[0]?.name; - const title = - uninitialized || loading ? ( - - ) : ( - name || groupId // in case of error, render just id from URL - ); - - const history = useHistory(); + const nameOrId = uninitialized || loading ? ( + + ) : ( + // in case of error, render just id from URL + data?.results?.[0]?.name || groupId + ); return ( - setRenameModalOpen(false)} - modalState={{ - id: groupId, - name: name || groupId - }} - reloadData={() => dispatch(fetchGroupDetail(groupId))} - /> - setDeleteModalOpen(false)} - modalState={{ - id: groupId, - name: name || groupId - }} - reloadData={() => history.push('/groups')} - /> Groups - {title} + {nameOrId} - - - - - - setDropdownOpen(!dropdownOpen)} - autoFocus={false} - isOpen={dropdownOpen} - toggle={ - setDropdownOpen(isOpen)} - toggleVariant="secondary" - isDisabled={uninitialized || loading} - > - Group actions - - } - dropdownItems={[ - setRenameModalOpen(true)} - > - Rename - , - setDeleteModalOpen(true)} - > - Delete - - ]} - /> - - + ); }; diff --git a/src/components/InventoryGroupDetail/GroupDetailInfo.cy.js b/src/components/InventoryGroupDetail/GroupDetailInfo.cy.js deleted file mode 100644 index 9e6b91a30..000000000 --- a/src/components/InventoryGroupDetail/GroupDetailInfo.cy.js +++ /dev/null @@ -1,39 +0,0 @@ -import { mount } from '@cypress/react'; -import React from 'react'; -import { GroupDetailInfo } from './GroupDetailInfo'; - -const mountPage = (params) => - mount( - - ); - -describe('group detail information page', () => { - beforeEach(() => { - mountPage({ chrome: { isBeta: () => false } }); - }); - - it('title is rendered', () => { - cy.get('div[class="pf-c-card__title pf-c-title pf-m-lg card-title"]').should('have.text', 'User access configuration'); - }); - - it('button is present', () => { - cy.get('a[class="pf-c-button pf-m-secondary"]').should('have.text', 'Manage access'); - }); - - it('card text is present', () => { - cy.get('div[class="pf-c-card__body"]').should - ('have.text', 'Manage your inventory group user access configuration under Identity & Access Management > User Access.'); - }); - - describe('links', () => { - it('in stable environment', () => { - mountPage({ chrome: { isBeta: () => false } }); - cy.get('a').should('have.attr', 'href', '/iam/user-access'); - }); - - it('in beta environment', () => { - mountPage({ chrome: { isBeta: () => true } }); - cy.get('a').should('have.attr', 'href', '/beta/iam/user-access'); - }); - }); -}); diff --git a/src/components/InventoryGroupDetail/GroupDetailInfo.js b/src/components/InventoryGroupDetail/GroupDetailInfo.js index 78914050d..01a935077 100644 --- a/src/components/InventoryGroupDetail/GroupDetailInfo.js +++ b/src/components/InventoryGroupDetail/GroupDetailInfo.js @@ -1,46 +1,20 @@ -import { - Button, - Card, - CardTitle, - CardBody, - CardHeader, - CardActions } from '@patternfly/react-core'; +import { EmptyState, EmptyStateBody, Spinner } from '@patternfly/react-core'; +import { InvalidObject } from '@redhat-cloud-services/frontend-components'; import React from 'react'; -import PropTypes from 'prop-types'; -import ChromeLoader from '../../Utilities/ChromeLoader'; +import { useSelector } from 'react-redux'; -const GroupDetailInfo = ({ chrome }) => { - const path = `${chrome.isBeta() ? '/beta' : ''}/iam/user-access`; +const GroupDetailInfo = () => { + const { uninitialized, loading } = useSelector((state) => state.groupDetail); + + // TODO: implement according to mocks return ( - - - - - - - User access configuration - - - - Manage your inventory group user access configuration under - Identity & Access Management {'>'} User Access. - - + + + {uninitialized || loading ? : } + + ); }; -GroupDetailInfo.propTypes = { - chrome: PropTypes.object -}; - -const GroupDetailInfoWithChrome = () => ( - - - -); - -export { GroupDetailInfo }; -export default GroupDetailInfoWithChrome; +export default GroupDetailInfo; diff --git a/src/components/InventoryGroupDetail/GroupDetailSystems.js b/src/components/InventoryGroupDetail/GroupDetailSystems.js new file mode 100644 index 000000000..e9470ce71 --- /dev/null +++ b/src/components/InventoryGroupDetail/GroupDetailSystems.js @@ -0,0 +1,22 @@ +import { EmptyState, EmptyStateBody, Spinner } from '@patternfly/react-core'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import NoSystemsEmptyState from './NoSystemsEmptyState'; + +const GroupDetailSystems = () => { + const { uninitialized, loading } = useSelector((state) => state.groupDetail); + + // TODO: integrate the inventory table + + return (uninitialized || loading ? + + + + + + : + + ); +}; + +export default GroupDetailSystems; diff --git a/src/components/InventoryGroupDetail/InventoryGroupDetail.cy.js b/src/components/InventoryGroupDetail/InventoryGroupDetail.cy.js index db3ddd65d..b487e971b 100644 --- a/src/components/InventoryGroupDetail/InventoryGroupDetail.cy.js +++ b/src/components/InventoryGroupDetail/InventoryGroupDetail.cy.js @@ -1,10 +1,9 @@ import { mount } from '@cypress/react'; -import { DROPDOWN, DROPDOWN_ITEM, MODAL } from '@redhat-cloud-services/frontend-components-utilities'; import React from 'react'; import { Provider } from 'react-redux'; import { MemoryRouter } from 'react-router-dom'; import groupDetailFixtures from '../../../cypress/fixtures/groups/620f9ae75A8F6b83d78F3B55Af1c4b2C.json'; -import { groupDetailInterceptors as interceptors, groupsInterceptors } from '../../../cypress/support/interceptors'; +import { groupDetailInterceptors as interceptors } from '../../../cypress/support/interceptors'; import { getStore } from '../../store'; import InventoryGroupDetail from './InventoryGroupDetail'; @@ -47,8 +46,7 @@ describe('group detail page', () => { .should('have.text', groupDetailFixtures.results[0].name); }); - /* TODO: fix this test (affected the execution of the next tests and made them flaky) - it('skeletons rendered while fetching data', () => { + it('skeletons rendered while fetching data', () => { // TODO: after each hook fails for some reason for this particular test Cypress.on('uncaught:exception', () => { return false; @@ -61,39 +59,4 @@ describe('group detail page', () => { cy.get('h1').find('.pf-c-skeleton'); cy.get('.pf-c-empty-state').find('.pf-c-spinner'); }); - */ - - it('can open rename group modal', () => { - interceptors.successful(); - interceptors['patch successful'](); - groupsInterceptors['successful with some items'](); // intercept modal validation requests - mountPage(); - - cy.wait('@getGroupDetail'); - - cy.get(DROPDOWN).click(); - cy.get(DROPDOWN_ITEM).contains('Rename').click(); - - cy.get(MODAL).find('input').type('1'); - cy.get(MODAL).find('button[type=submit]').click(); - - cy.wait('@patchGroup').its('request.body') - .should('deep.equal', { name: `${groupDetailFixtures.results[0].name}1` }); - cy.wait('@getGroupDetail'); // the page is refreshed after submition - }); - - it('can open delete group modal', () => { - interceptors.successful(); - interceptors['delete successful'](); - mountPage(); - cy.wait('@getGroupDetail'); - - cy.get(DROPDOWN).click(); - cy.get(DROPDOWN_ITEM).contains('Delete').click(); - - cy.get(`div[class="pf-c-check"]`).click(); - cy.get(`button[type="submit"]`).click(); - cy.wait('@deleteGroup').its('request.url') - .should('contain', groupDetailFixtures.results[0].id); - }); }); diff --git a/src/components/InventoryGroupDetail/InventoryGroupDetail.js b/src/components/InventoryGroupDetail/InventoryGroupDetail.js index bc414da7b..816d81043 100644 --- a/src/components/InventoryGroupDetail/InventoryGroupDetail.js +++ b/src/components/InventoryGroupDetail/InventoryGroupDetail.js @@ -1,17 +1,19 @@ +import React, { lazy, Suspense, useEffect } from 'react'; +import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { useDispatch, useSelector } from 'react-redux'; +import { fetchGroupDetail } from '../../store/inventory-actions'; import { Bullseye, PageSection, Spinner, Tab, - Tabs + Tabs, + TabTitleText } from '@patternfly/react-core'; -import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; -import PropTypes from 'prop-types'; -import React, { lazy, Suspense, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { fetchGroupDetail } from '../../store/inventory-actions'; -import GroupSystems from '../GroupSystems'; +import { useState } from 'react'; import GroupDetailHeader from './GroupDetailHeader'; +import GroupDetailSystems from './GroupDetailSystems'; +import PropTypes from 'prop-types'; const GroupDetailInfo = lazy(() => import('./GroupDetailInfo')); @@ -19,7 +21,6 @@ const InventoryGroupDetail = ({ groupId }) => { const dispatch = useDispatch(); const { data } = useSelector((state) => state.groupDetail); const chrome = useChrome(); - const groupName = data?.results?.[0]?.name; useEffect(() => { dispatch(fetchGroupDetail(groupId)); @@ -28,7 +29,7 @@ const InventoryGroupDetail = ({ groupId }) => { useEffect(() => { // if available, change ID to the group's name in the window title chrome?.updateDocumentTitle?.( - `${groupName || groupId} - Inventory Groups | Red Hat Insights` + `${data?.name || groupId} - Inventory Groups | Red Hat Insights` ); }, [data]); @@ -49,16 +50,16 @@ const InventoryGroupDetail = ({ groupId }) => { > Systems} aria-label="Group systems tab" > - + Group info} aria-label="Group info tab" > {activeTabKey === 1 && ( // helps to lazy load the component diff --git a/src/components/InventoryGroupDetail/NoSystemsEmptyState.js b/src/components/InventoryGroupDetail/NoSystemsEmptyState.js index 6ab6100c6..decc11181 100644 --- a/src/components/InventoryGroupDetail/NoSystemsEmptyState.js +++ b/src/components/InventoryGroupDetail/NoSystemsEmptyState.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { Button, EmptyState, @@ -10,24 +10,14 @@ import { import { ExternalLinkAltIcon, PlusCircleIcon } from '@patternfly/react-icons'; import { global_palette_black_600 as globalPaletteBlack600 } from '@patternfly/react-tokens/dist/js/global_palette_black_600'; -import AddSystemsToGroupModal from '../InventoryGroups/Modals/AddSystemsToGroupModal'; -import PropTypes from 'prop-types'; - -const NoSystemsEmptyState = ({ groupId, groupName }) => { - const [isModalOpen, setIsModalOpen] = useState(false); +const NoSystemsEmptyState = () => { return ( - No systems added @@ -35,14 +25,13 @@ const NoSystemsEmptyState = ({ groupId, groupName }) => { <EmptyStateBody> To manage systems more effectively, add systems to the group. </EmptyStateBody> - <Button variant="primary" onClick={() => setIsModalOpen(true)}> - Add systems - </Button> + <Button variant="primary" onClick={() => {}}>Add systems</Button> <EmptyStateSecondaryActions> <Button variant="link" icon={<ExternalLinkAltIcon />} iconPosition="right" + // TODO: component={(props) => <a href='' {...props} />} > Learn more about system groups </Button> @@ -50,8 +39,4 @@ const NoSystemsEmptyState = ({ groupId, groupName }) => { </EmptyState> );}; -NoSystemsEmptyState.propTypes = { - groupId: PropTypes.string, - groupName: PropTypes.string -}; export default NoSystemsEmptyState; diff --git a/src/components/InventoryGroupDetail/__tests__/GroupDetailHeader.test.js b/src/components/InventoryGroupDetail/__tests__/GroupDetailHeader.test.js index ecf4a1527..10a77cc0e 100644 --- a/src/components/InventoryGroupDetail/__tests__/GroupDetailHeader.test.js +++ b/src/components/InventoryGroupDetail/__tests__/GroupDetailHeader.test.js @@ -3,7 +3,6 @@ import { render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import GroupDetailHeader from '../GroupDetailHeader'; -import { DROPDOWN } from '@redhat-cloud-services/frontend-components-utilities/CypressUtils/selectors'; jest.mock('react-redux', () => { return { @@ -18,14 +17,12 @@ jest.mock('react-redux', () => { } ] } - }), - useDispatch: () => {} + }) }; }); describe('group detail header', () => { let getByRole; - let container; beforeEach(() => { const rendered = render( @@ -34,7 +31,6 @@ describe('group detail header', () => { </MemoryRouter> ); getByRole = rendered.getByRole; - container = rendered.container; }); it('renders title and breadcrumbs', () => { @@ -45,9 +41,4 @@ describe('group detail header', () => { expect(getByRole('navigation')).toHaveClass('pf-c-breadcrumb'); expect(getByRole('navigation')).toHaveTextContent('group-name-1'); }); - - it('renders the actions dropdown', () => { - expect(container.querySelector('#group-header-dropdown')).toHaveTextContent('Group actions'); - expect(container.querySelector(DROPDOWN)).toBeVisible(); - }); }); diff --git a/src/components/InventoryGroupDetail/__tests__/InventoryGroupDetail.test.js b/src/components/InventoryGroupDetail/__tests__/InventoryGroupDetail.test.js index 2b59832e3..44f42fa57 100644 --- a/src/components/InventoryGroupDetail/__tests__/InventoryGroupDetail.test.js +++ b/src/components/InventoryGroupDetail/__tests__/InventoryGroupDetail.test.js @@ -2,11 +2,25 @@ import '@testing-library/jest-dom'; import { render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { getStore } from '../../../store'; import InventoryGroupDetail from '../InventoryGroupDetail'; -import { Provider } from 'react-redux'; -jest.mock('../../../Utilities/useFeatureFlag'); +jest.mock('react-redux', () => { + return { + ...jest.requireActual('react-redux'), + useSelector: () => ({ + uninitialized: false, + loading: false, + data: { + results: [ + { + name: 'group-name-1' + } + ] + } + }), + useDispatch: () => () => {} + }; +}); describe('group detail page component', () => { let getByRole; @@ -15,9 +29,7 @@ describe('group detail page component', () => { beforeEach(() => { const rendered = render( <MemoryRouter> - <Provider store={getStore()}> - <InventoryGroupDetail groupId="group-id-2" /> - </Provider> + <InventoryGroupDetail groupId="group-id-2" /> </MemoryRouter> ); getByRole = rendered.getByRole; diff --git a/src/components/InventoryGroups/Modals/AddHostToGroupModal.js b/src/components/InventoryGroups/Modals/AddHostToGroupModal.js deleted file mode 100644 index 36ea5541d..000000000 --- a/src/components/InventoryGroups/Modals/AddHostToGroupModal.js +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import Modal from './Modal'; -import { addHostToGroup } from '../utils/api'; -import apiWithToast from '../utils/apiWithToast'; -import { useDispatch, useSelector } from 'react-redux'; -import { CreateGroupButton } from '../SmallComponents/CreateGroupButton'; -import { fetchGroups } from '../../../store/inventory-actions'; -import { addHostSchema } from './ModalSchemas/schemes'; -import CreateGroupModal from './CreateGroupModal'; - -const AddHostToGroupModal = ({ - isModalOpen, - setIsModalOpen, - modalState, - reloadData -}) => { - - const dispatch = useDispatch(); - //we have to fetch groups to make them available in state - useEffect(() => { - dispatch(fetchGroups()); - }, []); - - const groups = useSelector(({ groups }) => groups?.data?.results); - - const [isCreateGroupModalOpen, setIsCreateGroupModalOpen] = useState(false); - const handleAddDevices = (values) => { - const { group } = values; - const statusMessages = { - onSuccess: { - title: 'Success', - description: `System(s) have been added to ${group.name} successfully` - }, - onError: { title: 'Error', description: `Failed to add ${modalState.name} to ${group.name}` } - }; - - apiWithToast( - dispatch, - () => addHostToGroup(group.groupId, modalState.id), - statusMessages - ); - }; - - return ( - <> - <Modal - isModalOpen={isModalOpen} - closeModal={() => setIsModalOpen(false)} - title="Add to group" - submitLabel="Add" - schema={addHostSchema(modalState.name, groups)} - additionalMappers={{ - 'create-group-btn': { - component: CreateGroupButton, - closeModal: () => { - setIsCreateGroupModalOpen(true); - setIsModalOpen(false); - } - } - }} - initialValues={modalState} - onSubmit={handleAddDevices} - reloadData={reloadData} - /> - {isCreateGroupModalOpen && ( - <CreateGroupModal - isModalOpen={isCreateGroupModalOpen} - setIsModalOpen={setIsCreateGroupModalOpen} - reloadData={() => console.log('data reloaded')} - //modal before prop tells create group modal that it should - //reopen add host modal when user closes create group modal - modalBefore={true} - setterOfModalBefore={setIsModalOpen} - /> - )} - </> - ); -}; - -AddHostToGroupModal.propTypes = { - modalState: PropTypes.shape({ - id: PropTypes.string, - name: PropTypes.string, - groupName: PropTypes.string - }), - isModalOpen: PropTypes.bool, - setIsModalOpen: PropTypes.func, - reloadData: PropTypes.func, - setIsCreateGroupModalOpen: PropTypes.func, - deviceIds: PropTypes.array -}; - -export default AddHostToGroupModal; diff --git a/src/components/InventoryGroups/Modals/AddSystemsToGroupModal.cy.js b/src/components/InventoryGroups/Modals/AddSystemsToGroupModal.cy.js deleted file mode 100644 index c9dd8e37b..000000000 --- a/src/components/InventoryGroups/Modals/AddSystemsToGroupModal.cy.js +++ /dev/null @@ -1,184 +0,0 @@ -import { mount } from '@cypress/react'; -import { - checkTableHeaders, - DROPDOWN_ITEM, - MODAL, - ouiaId, - TABLE -} from '@redhat-cloud-services/frontend-components-utilities'; -import FlagProvider from '@unleash/proxy-client-react'; -import _ from 'lodash'; -import React from 'react'; -import { Provider } from 'react-redux'; -import { MemoryRouter } from 'react-router-dom'; -import { - featureFlagsInterceptors, - groupDetailInterceptors, - hostsFixtures, - hostsInterceptors -} from '../../../../cypress/support/interceptors'; -import { - selectRowN, - unleashDummyConfig -} from '../../../../cypress/support/utils'; -import { getStore } from '../../../store'; -import AddSystemsToGroupModal from './AddSystemsToGroupModal'; - -const TABLE_HEADERS = [ - 'Name', - 'Group', - 'Tags', - 'OS', - 'Update method', - 'Last seen' -]; - -const AVAILABLE_FILTER_NAMES = [ - 'Name', - 'Status', - 'Operating System', - 'Data Collector', - 'RHC status', - 'Last seen', - 'Group', - 'Tags' -]; - -const ALERT = '[data-ouia-component-type="PF4/Alert"]'; - -before(() => { - cy.window().then( - (window) => - (window.insights = { - chrome: { - isProd: false, - auth: { - getUser: () => { - return Promise.resolve({}); - } - } - } - }) - ); -}); - -const mountModal = () => - mount( - <FlagProvider config={unleashDummyConfig}> - <Provider store={getStore()}> - <MemoryRouter> - <AddSystemsToGroupModal - isModalOpen={true} - groupId="620f9ae75A8F6b83d78F3B55Af1c4b2C" - setIsModalOpen={() => {}} // TODO: test that the func is called on close - /> - </MemoryRouter> - </Provider> - </FlagProvider> - ); - -describe('test data', () => { - it('at least one system is already in a group', () => { - const alreadyInGroup = hostsFixtures.results.filter( - // eslint-disable-next-line camelcase - ({ group_name }) => !_.isEmpty(group_name) - ); - expect(alreadyInGroup.length).to.be.gte(1); - }); - - it('the first system in group has specific id', () => { - const alreadyInGroup = hostsFixtures.results.filter( - // eslint-disable-next-line camelcase - ({ group_name }) => !_.isEmpty(group_name) - ); - expect(alreadyInGroup[0].id).to.eq('anim commodo'); - }); -}); - -describe('AddSystemsToGroupModal', () => { - beforeEach(() => { - cy.viewport(1920, 1080); // to accomadate the inventory table - cy.intercept('*', { statusCode: 200 }); - hostsInterceptors.successful(); // default hosts list - featureFlagsInterceptors.successful(); // to enable the Group column - }); - - it('renders correct header and buttons', () => { - mountModal(); - - cy.wait('@getHosts'); - cy.get('h1').contains('Add systems'); - cy.get('button').contains('Add systems'); - cy.get('button').contains('Cancel'); - }); - - it('renders the inventory table', () => { - mountModal(); - - cy.wait('@getHosts'); - cy.get(ouiaId('PrimaryToolbar')); - cy.get(TABLE); - cy.get('#options-menu-bottom-pagination'); - checkTableHeaders(TABLE_HEADERS); - }); - - it('can add systems that are not yet in group', () => { - groupDetailInterceptors['patch successful'](); - groupDetailInterceptors['successful with hosts'](); - mountModal(); - - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - cy.get('button').contains('Add systems').should('be.disabled'); - selectRowN(1); - cy.get('button').contains('Add systems').click(); - cy.wait('@getGroupDetail'); // requests the current hosts list - cy.wait('@patchGroup') - .its('request.body') - .should('deep.equal', { - // eslint-disable-next-line camelcase - host_ids: ['host-1', 'host-2', 'dolor'] // sends the merged list of hosts - }); - }); - - it('can add systems that are already in group', () => { - groupDetailInterceptors['patch successful'](); - groupDetailInterceptors['successful with hosts'](); - mountModal(); - - cy.get('table[aria-label="Host inventory"]').should('have.attr', 'data-ouia-safe', 'true'); - const i = - hostsFixtures.results.findIndex( - // eslint-disable-next-line camelcase - ({ group_name }) => !_.isEmpty(group_name) - ) + 1; - selectRowN(i); - cy.get(ALERT); // check the alert is shown - cy.get('button').contains('Add systems').click(); - cy.get(MODAL).find('h1').contains('Add all selected systems to group?'); - cy.get('button') - .contains('Yes, add all systems to group') - .should('be.disabled'); - cy.get('input[name="confirmation"]').check(); - cy.get('button').contains('Yes, add all systems to group').click(); - cy.wait('@getGroupDetail'); - cy.wait('@patchGroup') - .its('request.body') - .should('deep.equal', { - // eslint-disable-next-line camelcase - host_ids: ['host-1', 'host-2', 'anim commodo'] // sends the merged list of hosts - }); - }); - - describe('filters', () => { - it('has correct list of filters', () => { - groupDetailInterceptors['successful with hosts'](); - mountModal(); - - cy.wait('@getHosts'); - cy.get('button[data-ouia-component-id="ConditionalFilter"]').click(); - cy.get(DROPDOWN_ITEM).each(($item, i) => { - expect($item.text()).to.equal(AVAILABLE_FILTER_NAMES[i]); - }); - }); - }); -}); diff --git a/src/components/InventoryGroups/Modals/AddSystemsToGroupModal.js b/src/components/InventoryGroups/Modals/AddSystemsToGroupModal.js deleted file mode 100644 index 5d092d2a6..000000000 --- a/src/components/InventoryGroups/Modals/AddSystemsToGroupModal.js +++ /dev/null @@ -1,169 +0,0 @@ -import { - Alert, - Button, - Flex, - FlexItem, - Modal -} from '@patternfly/react-core'; -import { TableVariant } from '@patternfly/react-table'; -import difference from 'lodash/difference'; -import map from 'lodash/map'; -import PropTypes from 'prop-types'; -import React, { useCallback, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { fetchGroupDetail } from '../../../store/inventory-actions'; -import { bulkSelectConfig, prepareColumns } from '../../GroupSystems/GroupSystems'; -import InventoryTable from '../../InventoryTable/InventoryTable'; -import { addHostsToGroupById } from '../utils/api'; -import apiWithToast from '../utils/apiWithToast'; -import ConfirmSystemsAddModal from './ConfirmSystemsAddModal'; - -const AddSystemsToGroupModal = ({ - isModalOpen, - setIsModalOpen, - groupId, - groupName -}) => { - const dispatch = useDispatch(); - - const [confirmationModalOpen, setConfirmationModalOpen] = useState(false); - const [systemsSelectModalOpen, setSystemSelectModalOpen] = useState(true); - const selected = useSelector( - (state) => state?.entities?.selected || new Map() - ); - const rows = useSelector(({ entities }) => entities?.rows || []); - - const noneSelected = selected.size === 0; - const displayedIds = map(rows, 'id'); - const pageSelected = difference(displayedIds, [...selected.keys()]).length === 0; - - const alreadyHasGroup = [...selected].filter( - // eslint-disable-next-line camelcase - (entry) => { - return entry[1].group_name !== undefined && entry[1].group_name !== ''; - } - ); - const showWarning = alreadyHasGroup.length > 0; - - const handleSystemAddition = useCallback( - (hostIds) => { - const statusMessages = { - onSuccess: { - title: 'Success', - description: `${hostIds.length > 1 ? 'Systems' : 'System'} added to ${groupName || groupId}` - }, - onError: { - title: 'Error', - description: `Failed to add ${hostIds.length > 1 ? 'systems' : 'system'} to ${groupName || groupId}` - } - }; - return apiWithToast( - dispatch, - () => addHostsToGroupById(groupId, hostIds), - statusMessages - ); - }, - [isModalOpen] - ); - - return ( - isModalOpen && ( - <> - {/** confirmation modal */} - <ConfirmSystemsAddModal - isModalOpen={confirmationModalOpen} - onSubmit={async () => { - await handleSystemAddition([...selected.keys()]); - setTimeout(() => dispatch(fetchGroupDetail(groupId)), 500); // refetch data for this group - setIsModalOpen(false); - - }} - onBack={() => { - setConfirmationModalOpen(false); - setSystemSelectModalOpen(true); // switch back to the systems table modal - }} - onCancel={() => setIsModalOpen(false)} - hostsNumber={alreadyHasGroup.length} - /> - {/** hosts selection modal */} - <Modal - title="Add systems" - isOpen={systemsSelectModalOpen} - onClose={() => setIsModalOpen(false)} - footer={ - <Flex direction={{ default: 'column' }} style={{ width: '100%' }}> - {showWarning && ( - <FlexItem fullWidth={{ default: 'fullWidth' }}> - <Alert - variant="warning" - isInline - title="One or more of the selected systems - already belong to a group. Adding these systems - to a different group may impact system configuration." - /> - </FlexItem> - )} - <FlexItem> - <Button - key="confirm" - variant="primary" - onClick={async () => { - if (showWarning) { - setSystemSelectModalOpen(false); - setConfirmationModalOpen(true); // switch to the confirmation modal - } else { - await handleSystemAddition([ - ...selected.keys() - ]); - setTimeout( - () => - dispatch( - fetchGroupDetail(groupId) - ), - 500 - ); // refetch data for this group - setIsModalOpen(false); - } - }} - isDisabled={noneSelected} - > - Add systems - </Button> - <Button - key="cancel" - variant="link" - onClick={() => setIsModalOpen(false)} - > - Cancel - </Button> - </FlexItem> - </Flex> - } - variant="large" // required to accomodate the systems table - > - <InventoryTable - columns={(columns) => prepareColumns(columns, false)} - variant={TableVariant.compact} // TODO: this doesn't affect the table variant - tableProps={{ - isStickyHeader: false, - canSelectAll: false - }} - bulkSelect={bulkSelectConfig(dispatch, selected.size, noneSelected, pageSelected, rows.length)} - initialLoading={true} - showTags - /> - </Modal> - </> - ) - ); -}; - -AddSystemsToGroupModal.propTypes = { - isModalOpen: PropTypes.bool, - setIsModalOpen: PropTypes.func, - reloadData: PropTypes.func, - groupId: PropTypes.string, - groupName: PropTypes.string -}; - -export default AddSystemsToGroupModal; diff --git a/src/components/InventoryGroups/Modals/ConfirmSystemsAddModal.js b/src/components/InventoryGroups/Modals/ConfirmSystemsAddModal.js deleted file mode 100644 index 48682b155..000000000 --- a/src/components/InventoryGroups/Modals/ConfirmSystemsAddModal.js +++ /dev/null @@ -1,74 +0,0 @@ -import { FormSpy, useFormApi } from '@data-driven-forms/react-form-renderer'; -import { Button, Flex } from '@patternfly/react-core'; -import ExclamationTriangleIcon from '@patternfly/react-icons/dist/js/icons/exclamation-triangle-icon'; -import warningColor from '@patternfly/react-tokens/dist/esm/global_warning_color_100'; -import PropTypes from 'prop-types'; -import React from 'react'; -import Modal from './Modal'; -import { confirmSystemsAddSchema } from './ModalSchemas/schemes'; - -const ConfirmSystemsAddModal = ({ - isModalOpen, - onSubmit, - onBack, - onCancel, - hostsNumber -}) => ( - <Modal - isModalOpen={isModalOpen} - title={'Add all selected systems to group?'} - titleIconVariant={() => ( - <ExclamationTriangleIcon color={warningColor.value} /> - )} - closeModal={onCancel} - schema={confirmSystemsAddSchema(hostsNumber)} - reloadData={() => {}} - onSubmit={onSubmit} - customFormTemplate={({ formFields, schema }) => { - const { handleSubmit, getState } = useFormApi(); - const { submitting, valid } = getState(); - - return ( - <form onSubmit={handleSubmit}> - <Flex - direction={{ default: 'column' }} - spaceItems={{ default: 'spaceItemsLg' }} - > - {schema.title} - {formFields} - <FormSpy> - {() => ( - <Flex> - <Button - isDisabled={submitting || !valid} - type="submit" - color="primary" - variant="primary" - > - Yes, add all systems to group - </Button> - <Button variant="secondary" onClick={onBack}> - Back - </Button> - <Button variant="link" onClick={onCancel}> - Cancel - </Button> - </Flex> - )} - </FormSpy> - </Flex> - </form> - ); - }} - /> -); - -ConfirmSystemsAddModal.propTypes = { - isModalOpen: PropTypes.bool, - onSubmit: PropTypes.func, - onBack: PropTypes.func, - onCancel: PropTypes.func, - hostsNumber: PropTypes.number -}; - -export default ConfirmSystemsAddModal; diff --git a/src/components/InventoryGroups/Modals/CreateGroupModal.js b/src/components/InventoryGroups/Modals/CreateGroupModal.js index 678e20d68..0a9d2ea88 100644 --- a/src/components/InventoryGroups/Modals/CreateGroupModal.js +++ b/src/components/InventoryGroups/Modals/CreateGroupModal.js @@ -13,9 +13,7 @@ import awesomeDebouncePromise from 'awesome-debounce-promise'; const CreateGroupModal = ({ isModalOpen, setIsModalOpen, - reloadData, - modalBefore = false, - setterOfModalBefore + reloadData }) => { const dispatch = useDispatch(); @@ -48,19 +46,11 @@ const CreateGroupModal = ({ return createGroupSchema(d); }, []); - const onClose = () => { - if (modalBefore) { - setIsModalOpen(false); - setterOfModalBefore(true); - } else { - setIsModalOpen(false); - } - }; - return ( <Modal + data-testid="create-group-modal" isModalOpen={isModalOpen} - closeModal={onClose} + closeModal={() => setIsModalOpen(false)} title="Create group" submitLabel="Create" schema={schema} @@ -75,7 +65,5 @@ export default CreateGroupModal; CreateGroupModal.propTypes = { isModalOpen: PropTypes.bool, setIsModalOpen: PropTypes.func, - reloadData: PropTypes.func, - modalBefore: PropTypes.bool, - setterOfModalBefore: PropTypes.func + reloadData: PropTypes.func }; diff --git a/src/components/InventoryGroups/Modals/Modal.js b/src/components/InventoryGroups/Modals/Modal.js index 71bcd3300..4f7380b2f 100644 --- a/src/components/InventoryGroups/Modals/Modal.js +++ b/src/components/InventoryGroups/Modals/Modal.js @@ -16,9 +16,7 @@ const RepoModal = ({ variant, reloadData, size, - onSubmit, - customFormTemplate, - additionalMappers + onSubmit }) => { return ( <Modal @@ -31,7 +29,7 @@ const RepoModal = ({ > <FormRenderer schema={schema} - FormTemplate={customFormTemplate ? customFormTemplate : (props) => ( + FormTemplate={(props) => ( <FormTemplate {...props} submitLabel={submitLabel} @@ -42,9 +40,7 @@ const RepoModal = ({ /> )} initialValues={initialValues} - componentMapper={additionalMappers - ? { ...additionalMappers, ...componentMapper } - : componentMapper} + componentMapper={componentMapper} //reload comes from the table and fetches fresh data onSubmit={async (values) => { await onSubmit(values); @@ -52,7 +48,6 @@ const RepoModal = ({ closeModal(); }} onCancel={() => closeModal()} - subscription={{ values: true }} /> </Modal> ); @@ -71,7 +66,7 @@ RepoModal.propTypes = { size: PropTypes.string, additionalMappers: PropTypes.object, titleIconVariant: PropTypes.any, - customFormTemplate: PropTypes.node + validatorMapper: PropTypes.object }; export default RepoModal; diff --git a/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js b/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js index 7d52f187e..1a5e306c1 100644 --- a/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js +++ b/src/components/InventoryGroups/Modals/ModalSchemas/schemes.js @@ -1,8 +1,6 @@ -import React from 'react'; import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types'; import componentTypes from '@data-driven-forms/react-form-renderer/component-types'; import { nameValidator } from '../../helpers/validate'; -import { Text } from '@patternfly/react-core'; export const createGroupSchema = (namePresenceValidator) => ({ fields: [ @@ -24,57 +22,3 @@ export const createGroupSchema = (namePresenceValidator) => ({ } ] }); - -export const confirmSystemsAddSchema = (hostsNumber) => ({ - fields: [ - { - component: componentTypes.PLAIN_TEXT, - name: 'warning-message', - label: `${hostsNumber} of the systems you selected already belong to a group. - Moving them to a different group will impact their configuration.` - }, - { - component: componentTypes.CHECKBOX, - name: 'confirmation', - label: 'I acknowledge that this action cannot be undone.', - validate: [{ type: validatorTypes.REQUIRED }] - } - ] -}); - -const createDescription = (systemName) => { - return ( - <Text> - Select a group to add <strong>{systemName}</strong> to, or create a new one. - </Text> - ); -}; - -//this is a custom schema that is passed via additional mappers to the Modal component -//it allows to create custom item types in the modal - -export const addHostSchema = (systemName, groups) => ({ - fields: [ - { - component: componentTypes.PLAIN_TEXT, - name: 'description', - label: createDescription(systemName) - }, - { - component: 'select', - name: 'group', - label: 'Select a group', - simpleValue: true, - isSearchable: true, // enables typeahead - isRequired: true, - isClearable: true, - placeholder: 'Type or click to select a group', - options: (groups || []).map(({ id, name }) => ({ - label: name, - value: { name, id } - })), - validate: [{ type: validatorTypes.REQUIRED }] - }, - { component: 'create-group-btn', name: 'create-group-btn', isRequired: true } - ] -}); diff --git a/src/components/InventoryGroups/Modals/RenameGroupModal.cy.js b/src/components/InventoryGroups/Modals/RenameGroupModal.cy.js index 21559fa45..b0e270f81 100644 --- a/src/components/InventoryGroups/Modals/RenameGroupModal.cy.js +++ b/src/components/InventoryGroups/Modals/RenameGroupModal.cy.js @@ -8,9 +8,45 @@ import { import { Provider } from 'react-redux'; import { MemoryRouter } from 'react-router-dom'; import { getStore } from '../../../store'; -import groups from '../../../../cypress/fixtures/groups.json'; -const mockResponse = [groups]; +const mockResponse = [ + { + count: 50, + page: 20, + per_page: 20, + total: 50, + results: [ + { + created_at: '2020-02-09T10:16:07.996Z', + host_ids: ['bA6deCFc19564430AB814bf8F70E8cEf'], + id: '3f01b55457674041b75e41829bcee1dca', + name: 'sre-group0', + updated_at: '2020-02-09T10:16:07.996Z' + }, + { + created_at: '2020-02-09T10:16:07.996Z', + host_ids: ['bA6deCFc19564430AB814bf8F70E8cEf'], + id: '3f01b55457674041b75e41829bcee1dca', + name: 'sre-group1', + updated_at: '2020-02-09T10:16:07.996Z' + }, + { + created_at: '2020-02-09T10:16:07.996Z', + host_ids: ['bA6deCFc19564430AB814bf8F70E8cEf'], + id: '3f01b55457674041b75e41829bcee1dca', + name: 'sre-group2', + updated_at: '2020-02-09T10:16:07.996Z' + }, + { + created_at: '2020-02-09T10:16:07.996Z', + host_ids: ['bA6deCFc19564430AB814bf8F70E8cEf'], + id: '3f01b55457674041b75e41829bcee1dca', + name: 'sre-group3', + updated_at: '2020-02-09T10:16:07.996Z' + } + ] + } +]; describe('render Rename Group Modal', () => { before(() => { @@ -38,21 +74,21 @@ describe('render Rename Group Modal', () => { } }).as('rename'); - }); - - it('Input is fillable and firing a validation request that succeeds', () => { mount( <MemoryRouter> <Provider store={getStore()}> <RenameGroupModal isModalOpen={true} reloadData={() => console.log('data reloaded')} - modalState={{ id: '1', name: 'Ut occaeca' }} + modalState={{ id: '1', name: 'sre-group' }} /> </Provider> </MemoryRouter> ); - cy.get(TEXT_INPUT).type('t'); + }); + + it('Input is fillable and firing a validation request that succeeds', () => { + cy.get(TEXT_INPUT).type('0'); cy.wait('@validate').then((xhr) => { expect(xhr.request.url).to.contain('groups');} ); @@ -60,17 +96,6 @@ describe('render Rename Group Modal', () => { }); it('User can rename the group', () => { - mount( - <MemoryRouter> - <Provider store={getStore()}> - <RenameGroupModal - isModalOpen={true} - reloadData={() => console.log('data reloaded')} - modalState={{ id: '1', name: 'sre-group' }} - /> - </Provider> - </MemoryRouter> - ); cy.get(TEXT_INPUT).type('newname'); cy.get(`button[type="submit"]`).should('have.attr', 'aria-disabled', 'false'); cy.get(`button[type="submit"]`).click(); diff --git a/src/components/InventoryGroups/Modals/RenameGroupModal.js b/src/components/InventoryGroups/Modals/RenameGroupModal.js index 8c1767a70..2fbcf9ba4 100644 --- a/src/components/InventoryGroups/Modals/RenameGroupModal.js +++ b/src/components/InventoryGroups/Modals/RenameGroupModal.js @@ -45,7 +45,7 @@ const RenameGroupModal = ({ }, onError: { title: 'Error', description: 'Failed to rename group' } }; - apiWithToast(dispatch, () => updateGroupById(id, { name: values.name }), statusMessages); + apiWithToast(dispatch, () => updateGroupById(id, values), statusMessages); }; const schema = useMemo(() => { diff --git a/src/components/InventoryGroups/SmallComponents/CreateGroupButton.js b/src/components/InventoryGroups/SmallComponents/CreateGroupButton.js deleted file mode 100644 index 93097edce..000000000 --- a/src/components/InventoryGroups/SmallComponents/CreateGroupButton.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { Button, Text } from '@patternfly/react-core'; -import PropTypes from 'prop-types'; - -export const CreateGroupButton = ({ closeModal }) => ( - <> - <Text>Or</Text> - <Button variant="secondary" className="pf-u-w-50" onClick={closeModal}> - Create a new group - </Button> - </> -); - -CreateGroupButton.propTypes = { - closeModal: PropTypes.func -}; diff --git a/src/components/InventoryGroups/utils/api.js b/src/components/InventoryGroups/utils/api.js index 2ae0ac650..4e28e0031 100644 --- a/src/components/InventoryGroups/utils/api.js +++ b/src/components/InventoryGroups/utils/api.js @@ -1,25 +1,15 @@ -/* eslint-disable camelcase */ import { instance } from '@redhat-cloud-services/frontend-components-utilities/interceptors/interceptors'; import { INVENTORY_API_BASE } from '../../../api'; import { TABLE_DEFAULT_PAGINATION } from '../../../constants'; import PropTypes from 'prop-types'; -import union from 'lodash/union'; -import fixtureGroups from '../../../../cypress/fixtures/groups.json'; -import fixtureGroupsDetails from '../../../../cypress/fixtures/groups/Ba8B79ab5adC8E41e255D5f8aDb8f1F3.json'; -// eslint-disable-next-line camelcase -export const getGroups = (search = {}, pagination = { page: 1, per_page: TABLE_DEFAULT_PAGINATION }) => { - if (window.Cypress) { - const parameters = new URLSearchParams({ - ...search, - ...pagination - }).toString(); +export const getGroups = (search = {}, pagination = { page: 1, perPage: TABLE_DEFAULT_PAGINATION }) => { + const parameters = new URLSearchParams({ + ...search, + ...pagination + }).toString(); - return instance.get(`${INVENTORY_API_BASE}/groups?${parameters}`); - } - - // FIXME: remove mock data when API is implemented - return Promise.resolve(fixtureGroups); + return instance.get(`${INVENTORY_API_BASE}/groups?${parameters}` /* , { headers: { Prefer: 'code=404' } } */); }; export const createGroup = (payload) => { @@ -31,46 +21,23 @@ export const createGroup = (payload) => { }; export const validateGroupName = (name) => { - if (window.Cypress) { - return instance.get(`${INVENTORY_API_BASE}/groups`) - .then((resp) => resp?.results.some((group) => group.name === name)); - } - - // FIXME: remove mock data when API is implemented - return Promise.resolve(fixtureGroups).then((resp) => resp?.results.some((group) => group.name === name)); + return instance.get(`${INVENTORY_API_BASE}/groups`) + .then((resp) => resp?.results.some((group) => group.name === name)); }; export const getGroupDetail = (groupId) => { - if (window.Cypress) { - return instance.get(`${INVENTORY_API_BASE}/groups/${groupId}`); - } - - // FIXME: remove mock data when API is implemented - return Promise.resolve(fixtureGroupsDetails); + return instance.get(`${INVENTORY_API_BASE}/groups/${groupId}`); }; export const updateGroupById = (id, payload) => { - return instance.patch(`${INVENTORY_API_BASE}/groups/${id}`, payload); + return instance.patch(`${INVENTORY_API_BASE}/groups/${id}`, { + name: payload.name + }); }; export const deleteGroupsById = (ids = []) => { return instance.delete(`${INVENTORY_API_BASE}/groups/${ids.join(',')}`); -}; - -export const addHostsToGroupById = (id, hostIds) => { - // the current hosts must be fetched before merging with the new ones - return getGroupDetail(id).then((response) => - updateGroupById(id, { - // eslint-disable-next-line camelcase - host_ids: union(response.results[0].host_ids, hostIds) - }) - ); -}; -export const addHostToGroup = (groupId, newHostId) => { - return instance.post(`${INVENTORY_API_BASE}/groups/${groupId}/hosts/${newHostId}`, { - host_ids: newHostId - }); }; getGroups.propTypes = { diff --git a/src/components/InventoryTable/EntityTableToolbar.js b/src/components/InventoryTable/EntityTableToolbar.js index 1d081566e..95f6b486f 100644 --- a/src/components/InventoryTable/EntityTableToolbar.js +++ b/src/components/InventoryTable/EntityTableToolbar.js @@ -19,9 +19,7 @@ import { TAG_CHIP, arrayToSelection, RHCD_FILTER_KEY, - UPDATE_METHOD_KEY, - LAST_SEEN_CHIP, - HOST_GROUP_CHIP + UPDATE_METHOD_KEY } from '../../Utilities/index'; import { onDeleteFilter, onDeleteTag } from './helpers'; import { @@ -30,7 +28,6 @@ import { useRegisteredWithFilter, useTagsFilter, useRhcdFilter, - useLastSeenFilter, useUpdateMethodFilter, textFilterState, textFilterReducer, @@ -43,16 +40,10 @@ import { rhcdFilterReducer, rhcdFilterState, updateMethodFilterReducer, - updateMethodFilterState, - lastSeenFilterReducer, - lastSeenFilterState, - groupFilterReducer, - groupFilterState + updateMethodFilterState } from '../filters'; import useOperatingSystemFilter from '../filters/useOperatingSystemFilter'; import useFeatureFlag from '../../Utilities/useFeatureFlag'; -import useGroupFilter from '../filters/useGroupFilter'; -import { DatePicker, Split, SplitItem } from '@patternfly/react-core'; /** * Table toolbar used at top of inventory table. @@ -90,18 +81,14 @@ const EntityTableToolbar = ({ tagsFilterReducer, operatingSystemFilterReducer, rhcdFilterReducer, - lastSeenFilterReducer, - updateMethodFilterReducer, - groupFilterReducer + updateMethodFilterReducer ]), { ...textFilterState, ...stalenessFilterState, ...registeredWithFilterState, ...tagsFilterState, ...rhcdFilterState, - ...updateMethodFilterState, - ...lastSeenFilterState, - ...groupFilterState + ...updateMethodFilterState }); const filters = useSelector(({ entities: { activeFilters } }) => activeFilters); const allTagsLoaded = useSelector(({ entities: { allTagsLoaded } }) => allTagsLoaded); @@ -111,15 +98,11 @@ const EntityTableToolbar = ({ const [stalenessFilter, stalenessChip, staleFilter, setStaleFilter] = useStalenessFilter(reducer); const [registeredFilter, registeredChip, registeredWithFilter, setRegisteredWithFilter] = useRegisteredWithFilter(reducer); const [rhcdFilterConfig, rhcdFilterChips, rhcdFilterValue, setRhcdFilterValue] = useRhcdFilter(reducer); - const [lastSeenFilter, lastSeenChip, lastSeenFilterValue, setLastSeenFilterValue, - toValidator, onFromChange, onToChange, endDate, startDate, fromValidator, - setStartDate, setEndDate] = useLastSeenFilter(reducer); const [osFilterConfig, osFilterChips, osFilterValue, setOsFilterValue] = useOperatingSystemFilter(); const [updateMethodConfig, updateMethodChips, updateMethodValue, setUpdateMethodValue] = useUpdateMethodFilter(reducer); - const [hostGroupConfig, hostGroupChips, hostGroupValue, setHostGroupValue] = useGroupFilter(); const isUpdateMethodEnabled = useFeatureFlag('hbi.ui.system-update-method'); - const groupsEnabled = useFeatureFlag('hbi.ui.inventory-groups'); + const { tagsFilter, tagsChip, @@ -135,7 +118,7 @@ const EntityTableToolbar = ({ const debounceGetAllTags = useCallback(debounce((config, options) => { if (showTags && !hasItems && hasAccess) { dispatch(fetchAllTags(config, { - ...options?.paginationhideFilters + ...options?.pagination }, getTags)); } }, 800), [customFilters?.tags]); @@ -147,13 +130,10 @@ const EntityTableToolbar = ({ operatingSystem: !(hideFilters.all && hideFilters.operatingSystem !== false) && !hideFilters.operatingSystem, tags: !(hideFilters.all && hideFilters.tags !== false) && !hideFilters.tags, rhcdFilter: !(hideFilters.all && hideFilters.rhcdFilter !== false) && !hideFilters.rhcdFilter, - lastSeenFilter: !(hideFilters.all && hideFilters.lastSeen !== false) && !hideFilters.lastSeen, //hides the filter untill API is ready. JIRA: RHIF-169 updateMethodFilter: isUpdateMethodEnabled && !(hideFilters.all && hideFilters.updateMethodFilter !== false) - && !hideFilters.updateMethodFilter, - hostGroupFilter: groupsEnabled && !(hideFilters.all && hideFilters.hostGroupFilter !== false) - && !hideFilters.hostGroupFilter + && !hideFilters.updateMethodFilter }; /** @@ -191,15 +171,7 @@ const EntityTableToolbar = ({ */ useEffect(() => { const { - textFilter, - tagFilters, - staleFilter, - registeredWithFilter, - osFilter, - rhcdFilter, - lastSeenFilter, - updateMethodFilter, - groupFilter + textFilter, tagFilters, staleFilter, registeredWithFilter, osFilter, rhcdFilter, updateMethodFilter } = reduceFilters([...filters || [], ...customFilters?.filters || []]); debouncedRefresh(); @@ -210,8 +182,6 @@ const EntityTableToolbar = ({ enabledFilters.operatingSystem && setOsFilterValue(osFilter); enabledFilters.rhcdFilter && setRhcdFilterValue(rhcdFilter); enabledFilters.updateMethodFilter && setUpdateMethodValue(updateMethodFilter); - enabledFilters.lastSeenFilter && setLastSeenFilterValue(lastSeenFilter); - enabledFilters.hostGroupFilter && setHostGroupValue(groupFilter); }, []); /** @@ -220,7 +190,7 @@ const EntityTableToolbar = ({ * @param {*} debounced if debounce function should be used. */ const onSetTextFilter = (value, debounced = true) => { - const trimmedValue = value?.trim(); + const trimmedValue = value.trim(); const textualFilter = filters?.find(oneFilter => oneFilter.value === TEXT_FILTER); if (textualFilter) { @@ -291,24 +261,12 @@ const EntityTableToolbar = ({ } }, [rhcdFilterValue]); - useEffect(() => { - if (shouldReload && enabledFilters.lastSeenFilter) { - onSetFilter(lastSeenFilterValue, 'lastSeenFilter', debouncedRefresh); - } - }, [lastSeenFilterValue]); - useEffect(() => { if (shouldReload && enabledFilters.updateMethodFilter) { onSetFilter(updateMethodValue, 'updateMethodFilter', debouncedRefresh); } }, [updateMethodValue]); - useEffect(() => { - if (shouldReload && enabledFilters.hostGroupFilter) { - onSetFilter(hostGroupValue, 'hostGroupFilter', debouncedRefresh); - } - }, [hostGroupValue]); - /** * Mapper to simplify removing of any filter. */ @@ -327,13 +285,7 @@ const EntityTableToolbar = ({ ), [OS_CHIP]: (deleted) => setOsFilterValue(xor(osFilterValue, deleted.chips.map(({ value }) => value))), [RHCD_FILTER_KEY]: (deleted) => setRhcdFilterValue(onDeleteFilter(deleted, rhcdFilterValue)), - [LAST_SEEN_CHIP]: (deleted) => - { - setLastSeenFilterValue(onDeleteFilter(deleted, [lastSeenFilterValue.mark])), - setStartDate(), - setEndDate(); - }, [UPDATE_METHOD_KEY]: (deleted) => setUpdateMethodValue(onDeleteFilter(deleted, updateMethodValue)), - [HOST_GROUP_CHIP]: (deleted) => setHostGroupValue(onDeleteFilter(deleted, hostGroupValue)) + [UPDATE_METHOD_KEY]: (deleted) => setUpdateMethodValue(onDeleteFilter(deleted, updateMethodValue)) }; /** * Function to reset all filters with 'Reset Filter' is clicked @@ -345,9 +297,7 @@ const EntityTableToolbar = ({ enabledFilters.tags && setSelectedTags({}); enabledFilters.operatingSystem && setOsFilterValue([]); enabledFilters.rhcdFilter && setRhcdFilterValue([]); - enabledFilters.lastSeenFilter && setLastSeenFilterValue([]); enabledFilters.updateMethodFilter && setUpdateMethodValue([]); - enabledFilters.hostGroupFilter && setHostGroupValue([]); dispatch(setFilter([])); updateData({ page: 1, filters: [] }); }; @@ -366,8 +316,6 @@ const EntityTableToolbar = ({ ...!hasItems && enabledFilters.operatingSystem ? osFilterChips : [], ...!hasItems && enabledFilters.rhcdFilter ? rhcdFilterChips : [], ...!hasItems && enabledFilters.updateMethodFilter ? updateMethodChips : [], - ...!hasItems && enabledFilters.lastSeenFilter ? lastSeenChip : [], - ...!hasItems && enabledFilters.hostGroupFilter ? hostGroupChips : [], ...activeFiltersConfig?.filters || [] ], onDelete: (e, [deleted, ...restDeleted], isAll) => { @@ -393,8 +341,6 @@ const EntityTableToolbar = ({ ...enabledFilters.registeredWith ? [registeredFilter] : [], ...enabledFilters.rhcdFilter ? [rhcdFilterConfig] : [], ...enabledFilters.updateMethodFilter ? [updateMethodConfig] : [], - ...enabledFilters.lastSeenFilter ? [lastSeenFilter] : [], - ...enabledFilters.hostGroupFilter ? [hostGroupConfig] : [], ...showTags && enabledFilters.tags ? [tagsFilter] : [] ] : [], ...filterConfig?.items || [] @@ -438,32 +384,7 @@ const EntityTableToolbar = ({ ...paginationProps } : <Skeleton size={SkeletonSize.lg} />} > - {lastSeenFilterValue?.mark === 'custom' && - <Split> - <SplitItem> - <DatePicker - onChange={onFromChange} - aria-label="Start date" - validators={[fromValidator]} - placeholder='Start' - /> - </SplitItem> - <SplitItem style={{ padding: '6px 12px 0 12px' }}> - to - </SplitItem> - <SplitItem> - <DatePicker - value={endDate} - onChange={onToChange} - rangeStart={startDate} - validators={[toValidator]} - aria-label="End date" - placeholder='End' - /> - </SplitItem> - </Split>} { children } - </PrimaryToolbar> { (showTags || enabledFilters.tags || showTagModal) && <TagsModal @@ -509,9 +430,7 @@ EntityTableToolbar.propTypes = { stale: PropTypes.bool, operatingSystem: PropTypes.bool, rhcdFilter: PropTypes.bool, - lastSeen: PropTypes.bool, updateMethodFilter: PropTypes.bool, - hostGroupFilter: PropTypes.bool, all: PropTypes.bool }), paginationProps: PropTypes.object, diff --git a/src/components/InventoryTable/EntityTableToolbar.test.js b/src/components/InventoryTable/EntityTableToolbar.test.js index 8a6b2fcc0..faa09fda1 100644 --- a/src/components/InventoryTable/EntityTableToolbar.test.js +++ b/src/components/InventoryTable/EntityTableToolbar.test.js @@ -14,12 +14,6 @@ import debounce from 'lodash/debounce'; jest.mock('lodash/debounce'); jest.mock('../../Utilities/useFeatureFlag'); -jest.mock('../../Utilities/constants', () => ({ - ...jest.requireActual('../../Utilities/constants'), - lastSeenItems: jest.fn().mockReturnValue([]) - -})); - describe('EntityTableToolbar', () => { let initialState; let stateWithActiveFilter; @@ -330,7 +324,7 @@ describe('EntityTableToolbar', () => { </Provider>); wrapper.find('.ins-c-chip-filters button.pf-m-link').last().simulate('click'); const actions = store.getActions(); - expect(actions.length).toBe(4); + expect(actions.length).toBe(3); expect(actions[actions.length - 2]).toMatchObject({ type: 'CLEAR_FILTERS' }); expect(onRefreshData).toHaveBeenCalledWith({ filters: [], page: 1 }); }); @@ -355,7 +349,7 @@ describe('EntityTableToolbar', () => { const wrapper = mount(<Provider store={store}> <EntityTableToolbar - hideFilters={{ all: true, name: false, group: true }} + hideFilters={{ all: true, name: false }} page={1} total={500} perPage={50} diff --git a/src/components/InventoryTable/InventoryTable.js b/src/components/InventoryTable/InventoryTable.js index 6dca45f4b..1565553a8 100644 --- a/src/components/InventoryTable/InventoryTable.js +++ b/src/components/InventoryTable/InventoryTable.js @@ -11,7 +11,6 @@ import AccessDenied from '../../Utilities/AccessDenied'; import { loadSystems } from '../../Utilities/sharedFunctions'; import isEqual from 'lodash/isEqual'; import { entitiesLoading } from '../../store/actions'; -import cloneDeep from 'lodash/cloneDeep'; /** * A helper function to store props and to always return the latest state. @@ -19,17 +18,14 @@ import cloneDeep from 'lodash/cloneDeep'; * to get the latest props and not the props at the time of when the function is * being wrapped in callback. */ -const inventoryCache = () => { +const propsCache = () => { let cache = {}; - const updateProps = (props) => { cache = cloneDeep({ ...cache, props }); }; + const updateProps = (props) => { cache = props; }; - const updateParams = (params) => { cache = cloneDeep({ ...cache, params }); }; + const getProps = () => cache; - const getProps = () => cache.props; - const getParams = () => cache.params; - - return { updateProps, updateParams, getProps, getParams }; + return { updateProps, getProps }; }; /** @@ -75,10 +71,12 @@ const InventoryTable = forwardRef(({ // eslint-disable-line react/display-name )); const page = useSelector(({ entities: { page: invPage } }) => ( hasItems ? propsPage : (invPage || 1) - ), shallowEqual); + ) + , shallowEqual); const perPage = useSelector(({ entities: { perPage: invPerPage } }) => ( hasItems ? propsPerPage : (invPerPage || 50) - ), shallowEqual); + ) + , shallowEqual); const total = useSelector(({ entities: { total: invTotal } }) => { if (hasItems) { return propsTotal !== undefined ? propsTotal : items?.length; @@ -114,7 +112,7 @@ const InventoryTable = forwardRef(({ // eslint-disable-line react/display-name const dispatch = useDispatch(); const store = useStore(); - const cache = useRef(inventoryCache()); + const cache = useRef(propsCache()); cache.current.updateProps({ page, perPage, @@ -136,7 +134,7 @@ const InventoryTable = forwardRef(({ // eslint-disable-line react/display-name const cachedProps = cache.current?.getProps() || {}; const currPerPage = options?.per_page || options?.perPage || cachedProps.perPage; - const newParams = { + const params = { page: cachedProps.page, per_page: currPerPage, items: cachedProps.items, @@ -148,29 +146,25 @@ const InventoryTable = forwardRef(({ // eslint-disable-line react/display-name ...options }; - const cachedParams = cache.current.getParams(); - if (!isEqual(cachedParams, newParams)) { - cache.current.updateParams(newParams); - if (onRefresh && !disableOnRefresh) { - dispatch(entitiesLoading()); - onRefresh(newParams, (options) => { - dispatch( - loadSystems( - { ...newParams, ...options }, - cachedProps.showTags, - cachedProps.getEntities - ) - ); - }); - } else { + if (onRefresh && !disableOnRefresh) { + dispatch(entitiesLoading()); + onRefresh(params, (options) => { dispatch( loadSystems( - newParams, + { ...params, ...options }, cachedProps.showTags, cachedProps.getEntities ) ); - } + }); + } else { + dispatch( + loadSystems( + params, + cachedProps.showTags, + cachedProps.getEntities + ) + ); } }; diff --git a/src/components/InventoryTable/InventoryTable.test.js b/src/components/InventoryTable/InventoryTable.test.js index 08eb6f2c0..64d25be34 100644 --- a/src/components/InventoryTable/InventoryTable.test.js +++ b/src/components/InventoryTable/InventoryTable.test.js @@ -220,14 +220,7 @@ describe('InventoryTable', () => { </Provider>); expect(wrapper.find(ConditionalFilter).props().items.map(({ label }) => label)).toEqual( - ['Status', - 'Operating System', - 'Data Collector', - 'RHC status', - 'System Update Method', - 'Last seen', - 'Group', - 'Tags'] + ['Status', 'Operating System', 'Data Collector', 'RHC status', 'System Update Method', 'Tags'] ); }); diff --git a/src/components/InventoryTable/TitleColumn.js b/src/components/InventoryTable/TitleColumn.js index c327a8a2f..ac41ddcf0 100644 --- a/src/components/InventoryTable/TitleColumn.js +++ b/src/components/InventoryTable/TitleColumn.js @@ -32,7 +32,7 @@ const onRowClick = (event, key, { loaded, onRowClick: rowClick, noDetail }) => { * @param {*} props additional props passed from `EntityTable` - holds any props passed to inventory table. */ const TitleColumn = (data, id, item, props) => ( - <div className="ins-composed-col sentry-mask data-hj-suppress"> + <div className="ins-composed-col"> <div key="os_release">{item?.os_release}</div> <div key="data" className={props?.noDetail ? 'ins-m-nodetail' : ''}> { props?.noDetail ? diff --git a/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap b/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap index 3169e0c6e..340c86783 100644 --- a/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap +++ b/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap @@ -102,7 +102,7 @@ exports[`EntityTable DOM should render correctly - compact 1`] = ` data-label="One" > <div - class="ins-composed-col sentry-mask data-hj-suppress" + class="ins-composed-col" > <div /> <div @@ -283,7 +283,7 @@ exports[`EntityTable DOM should render correctly - is expandable 1`] = ` "cells": Array [ <React.Fragment> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div /> <div @@ -468,7 +468,7 @@ exports[`EntityTable DOM should render correctly - with actions 1`] = ` "cells": Array [ <React.Fragment> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div /> <div @@ -601,7 +601,7 @@ exports[`EntityTable DOM should render correctly - without checkbox 1`] = ` "cells": Array [ <React.Fragment> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div /> <div @@ -737,7 +737,7 @@ exports[`EntityTable DOM should render correctly 1`] = ` data-label="One" > <div - class="ins-composed-col sentry-mask data-hj-suppress" + class="ins-composed-col" > <div /> <div @@ -808,7 +808,7 @@ exports[`EntityTable DOM sort by should render correctly - is expandable 1`] = ` "cells": Array [ <React.Fragment> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div /> <div @@ -887,7 +887,7 @@ exports[`EntityTable DOM sort by should render correctly - without checkbox 1`] "cells": Array [ <React.Fragment> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div /> <div @@ -967,7 +967,7 @@ exports[`EntityTable DOM sort by should render correctly 1`] = ` "cells": Array [ <React.Fragment> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div /> <div diff --git a/src/components/InventoryTable/__snapshots__/EntityTableToolbar.test.js.snap b/src/components/InventoryTable/__snapshots__/EntityTableToolbar.test.js.snap index 2e0e03e3e..7a54d42c1 100644 --- a/src/components/InventoryTable/__snapshots__/EntityTableToolbar.test.js.snap +++ b/src/components/InventoryTable/__snapshots__/EntityTableToolbar.test.js.snap @@ -127,18 +127,6 @@ exports[`EntityTableToolbar DOM should render correctly - no data 1`] = ` "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, ], } } @@ -316,18 +304,6 @@ exports[`EntityTableToolbar DOM should render correctly - with children 1`] = ` "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, ], } } @@ -522,18 +498,6 @@ exports[`EntityTableToolbar DOM should render correctly - with custom activeFilt "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, Object { "filterValues": Object { "className": "ins-c-tagfilter", @@ -746,18 +710,6 @@ exports[`EntityTableToolbar DOM should render correctly - with custom filters 1` "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, Object { "filterValues": Object { "isDisabled": false, @@ -960,18 +912,6 @@ exports[`EntityTableToolbar DOM should render correctly - with customFilters 1`] "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, Object { "filterValues": Object { "className": "ins-c-tagfilter", @@ -1197,18 +1137,6 @@ exports[`EntityTableToolbar DOM should render correctly - with default filters 1 "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, ], } } @@ -1391,18 +1319,6 @@ exports[`EntityTableToolbar DOM should render correctly - with default tag filte "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, ], } } @@ -1601,18 +1517,6 @@ exports[`EntityTableToolbar DOM should render correctly - with no access 1`] = ` "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": true, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, ], } } @@ -1803,18 +1707,6 @@ exports[`EntityTableToolbar DOM should render correctly - with tags 1`] = ` "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, Object { "filterValues": Object { "className": "ins-c-tagfilter", @@ -2035,18 +1927,6 @@ exports[`EntityTableToolbar DOM should render correctly 1`] = ` "type": "checkbox", "value": "rhc-status", }, - Object { - "filterValues": Object { - "isDisabled": false, - "items": [MockFunction], - "onChange": [Function], - "placeholder": "Filter by last seen", - "value": undefined, - }, - "label": "Last seen", - "type": "radio", - "value": "last_seen", - }, ], } } diff --git a/src/components/InventoryTable/__snapshots__/TitleColumn.test.js.snap b/src/components/InventoryTable/__snapshots__/TitleColumn.test.js.snap index 930d3903c..b1ce57c68 100644 --- a/src/components/InventoryTable/__snapshots__/TitleColumn.test.js.snap +++ b/src/components/InventoryTable/__snapshots__/TitleColumn.test.js.snap @@ -3,7 +3,7 @@ exports[`TitleColumn should render correctly no detail with data 1`] = ` <Cmp> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div key="os_release" @@ -23,7 +23,7 @@ exports[`TitleColumn should render correctly no detail with data 1`] = ` exports[`TitleColumn should render correctly with NO data 1`] = ` <Cmp> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div key="os_release" @@ -45,7 +45,7 @@ exports[`TitleColumn should render correctly with NO data 1`] = ` exports[`TitleColumn should render correctly with data 1`] = ` <Cmp> <div - className="ins-composed-col sentry-mask data-hj-suppress" + className="ins-composed-col" > <div key="os_release" diff --git a/src/components/InventoryTable/helpers.js b/src/components/InventoryTable/helpers.js index 1b181d88a..862c4d1d7 100644 --- a/src/components/InventoryTable/helpers.js +++ b/src/components/InventoryTable/helpers.js @@ -19,13 +19,6 @@ export const buildCells = (item, columns, extra) => { }); }; -//returns an array of objects representing rows for a table. -//The function takes three parameters: "rows", "columns", and an object with several optional properties. -//The "rows" parameter is an array of objects, where each object represents a single row. -//The "columns" parameter is also an array of objects, where each object represents a single column in the table. -//The third parameter is an object with several optional properties, including "actions", -//"expandable", "noSystemsTable", and "extra". These properties are destructured from -//the object using object destructuring syntax. export const createRows = (rows = [], columns = [], { actions, expandable, noSystemsTable, ...extra } = {}) => { if (rows.length === 0) { return [{ @@ -39,12 +32,6 @@ export const createRows = (rows = [], columns = [], { actions, expandable, noSys }]; } - //If the "rows" parameter is not empty, the function maps over each row object in the "rows" - //array and creates an array of two objects for each row. The first object represents the - //row itself and contains the "cells" property, which is an array of objects representing - //each cell in the row. The "actionProps" property is also set to an object containing the - //"data-ouia-component-id" property, which is set to a string combining the row's "id" property - //and the string "-actions-kebab". return flatten(rows.map((oneItem, key) => ([{ ...oneItem, ...oneItem.children && expandable && { isOpen: !!oneItem.isOpen }, @@ -52,13 +39,7 @@ export const createRows = (rows = [], columns = [], { actions, expandable, noSys actionProps: { 'data-ouia-component-id': `${oneItem.id}-actions-kebab` } - }, - //The second object represents the child row, which is only created if the "expandable" - //property is set to true and the row has a "children" property. This object has the - //"cells" property set to an array containing a single object representing the cell - //in the row. The "parent" property is set to the index of the parent row multiplied by 2, - //and the "fullWidth" property is set to true. - oneItem.children && expandable && { + }, oneItem.children && expandable && { cells: [ { title: typeof oneItem.children === 'function' ? oneItem.children() : oneItem.children diff --git a/src/components/InventoryTable/hooks/useColumns.js b/src/components/InventoryTable/hooks/useColumns.js index e267efb70..d2a92e02b 100644 --- a/src/components/InventoryTable/hooks/useColumns.js +++ b/src/components/InventoryTable/hooks/useColumns.js @@ -17,7 +17,6 @@ const useColumns = (columnsProp, disableDefaultColumns, showTags, columnsCounter ) ); const disabledColumns = Array.isArray(disableDefaultColumns) ? disableDefaultColumns : []; - //condition for the newDefaultColumns should be removed after inventory groups is released const defaultColumnsFiltered = useMemo(() => (disableDefaultColumns === true) ? [] : defaultColumns(groupsEnabled).filter(({ key }) => isColumnEnabled(key, disabledColumns, showTags) diff --git a/src/components/filters/__snapshots__/useGroupFilter.test.js.snap b/src/components/filters/__snapshots__/useGroupFilter.test.js.snap deleted file mode 100644 index eed91ba77..000000000 --- a/src/components/filters/__snapshots__/useGroupFilter.test.js.snap +++ /dev/null @@ -1,83 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`useGroupFilter with groups loaded should match snapshot 1`] = ` -Array [ - Object { - "filterValues": Object { - "items": Array [ - Object { - "label": "nisi ut consequat ad", - "value": "nisi ut consequat ad", - }, - Object { - "label": "nisi ut consequat ad1", - "value": "nisi ut consequat ad1", - }, - Object { - "label": "nisi ut consequat ad2", - "value": "nisi ut consequat ad2", - }, - ], - "onChange": [Function], - "value": Array [], - }, - "label": "Group", - "type": "checkbox", - "value": "group-host-filter", - }, - Array [], - Array [], - [Function], -] -`; - -exports[`useGroupFilter with groups loaded should return correct chips array, current value and value setter 1`] = ` -Array [ - Object { - "category": "Group", - "chips": Array [ - Object { - "name": "nisi ut consequat ad1", - "value": "nisi ut consequat ad1", - }, - ], - "type": "host_group", - }, -] -`; - -exports[`useGroupFilter with groups loaded should return empty state value if no groups obtained 1`] = ` -Array [ - Object { - "filterValues": Object { - "items": Array [], - "onChange": [Function], - "value": Array [], - }, - "label": "Group", - "type": "checkbox", - "value": "group-host-filter", - }, - Array [], - Array [], - [Function], -] -`; - -exports[`useGroupFilter with groups yet not loaded should return empty state value 1`] = ` -Array [ - Object { - "filterValues": Object { - "items": Array [], - "onChange": [Function], - "value": Array [], - }, - "label": "Group", - "type": "checkbox", - "value": "group-host-filter", - }, - Array [], - Array [], - [Function], -] -`; diff --git a/src/components/filters/index.js b/src/components/filters/index.js index 69e83b911..4c9fae5db 100644 --- a/src/components/filters/index.js +++ b/src/components/filters/index.js @@ -5,8 +5,6 @@ export * from './useTagsFilter'; export * from './useOperatingSystemFilter'; export * from './useRhcdFilter'; export * from './useUpdateMethodFilter'; -export * from './useLastSeenFilter'; -export * from './useGroupFilter'; export const filtersReducer = (reducersList) => (state, action) => reducersList.reduce((acc, curr) => ({ ...acc, ...curr?.(state, action) diff --git a/src/components/filters/useGroupFilter.js b/src/components/filters/useGroupFilter.js deleted file mode 100644 index 44a3d2b98..000000000 --- a/src/components/filters/useGroupFilter.js +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable camelcase */ -import union from 'lodash/union'; -import { useEffect, useMemo, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { fetchGroups } from '../../store/inventory-actions'; -import { HOST_GROUP_CHIP } from '../../Utilities/index'; - -//for attaching this filter to the redux -export const groupFilterState = { groupHostFilter: null }; -export const GROUP_FILTER = 'GROUP_FILTER'; -export const groupFilterReducer = (_state, { type, payload }) => ({ - ...type === GROUP_FILTER && { - groupHostFilter: payload - } -}); - -//receive the array of selected groups and return chips based on the name of selected groups -export const buildHostGroupChips = (selectedGroups = []) => { - //we use new Set to make sure that chips are unique - const chips = [...selectedGroups]?.map((group) => ({ name: group, value: group })); - return chips?.length > 0 - ? [ - { - category: 'Group', - type: HOST_GROUP_CHIP, - chips - } - ] - : []; -}; - -const useGroupFilter = (apiParams = []) => { - const dispatch = useDispatch(); - useEffect(() => { - dispatch(fetchGroups(apiParams)); - }, []); - //fetched values - const fetchedValues = useSelector(({ groups }) => groups?.data?.results); - //selected are the groups we selected - const [selected, setSelected] = useState([]); - //buildHostGroupsValues build an array of objects to populate dropdown - const [buildHostGroupsValues, setBuildHostGroupsValues] = useState([]); - //hostGroupValue is used for config items - useEffect(() => { - setBuildHostGroupsValues((fetchedValues || []).reduce((acc, group) => { - acc.push({ label: group.name, value: group.name }); - return acc; - }, [])); - }, [fetchedValues, selected]); - //this is used in the filter config as a way to select values onChange - const onHostGroupsChange = (event, selection, item) => { - setSelected(union(selection, item)); - }; - - const chips = useMemo(() => buildHostGroupChips(selected), [selected]); - //chips that is built for the filter config - - //hostGroupConfig is a config that we use in EntityTableToolbar.js - const hostGroupConfig = useMemo(() => ({ - label: 'Group', - value: 'group-host-filter', - type: 'checkbox', - filterValues: { - onChange: (event, value, item) => { - onHostGroupsChange(event, value, item); - }, - value: selected, - items: buildHostGroupsValues - } - }), [selected, buildHostGroupsValues]); - - //setSelectedValues is used for selecting and deleting values - const setSelectedValues = (currentValue = []) => { - setSelected(currentValue); - }; - - return [hostGroupConfig, chips, selected, setSelectedValues]; -}; - -export default useGroupFilter; diff --git a/src/components/filters/useGroupFilter.test.js b/src/components/filters/useGroupFilter.test.js deleted file mode 100644 index 0c7e6880a..000000000 --- a/src/components/filters/useGroupFilter.test.js +++ /dev/null @@ -1,176 +0,0 @@ -/* eslint-disable camelcase */ -import { act, renderHook } from '@testing-library/react-hooks'; -import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import { createPromise as promiseMiddleware } from 'redux-promise-middleware'; -import { mockSystemProfile } from '../../__mocks__/hostApi'; -import useGroupFilter from './useGroupFilter'; - -describe('useGroupFilter', () => { - const mockStore = configureStore([promiseMiddleware()]); - beforeEach(() => { - mockSystemProfile.onGet().replyOnce(200); - }); - - describe('with groups yet not loaded', () => { - const wrapper = ({ children }) => ( - <Provider store={mockStore({})}> - {children} - </Provider> - ); - - it('should return empty state value', () => { - const { result } = renderHook(useGroupFilter, { wrapper }); - expect(result.current).toMatchSnapshot(); - }); - }); - - describe('with groups loaded', () => { - const wrapper = ({ children }) => ( - <Provider - store={mockStore({ - groups: { - data: { - page: 1, - count: 50, - results: [{ - created_at: '2019-02-18T23:00:00.0Z', - id: '4f5B88dBe1D4eB732B388abc1Baa4BAc', - updated_at: '1960-11-06T23:00:00.0Z', - org_id: 'nostrud in deserunt', - account: 'ullamco dolore pariatur sint', - host_ids: [ - '5f20569C-9E96-C794-887c-1Cb5D6e220b2', - 'D34e68beFc2d4fF1C19CF4CaF913d1b6', - '76D3AcBB-5B4C-8BB8-f3BB-69808CcFa84A', - '185dC76d-D6B8-059b-1bCb-685D2edd0CEa', - '50E0cAaDCace55e07AebC7Bcc40cfdAD', - 'be8e0A09AdEE9a23fe65972F43D31bDA', - '8B6764001Bf6d0bA4E6aEdCAf207bf71', - '80CffFbd-04C9-cBB6-5Bf5-Dc0eBbDBba70', - '55d8323C-dEbC-B10e-f88e-23ba37068C9f', - 'C5A8Fd55a0B6fa3A3a748cb0a4cc20BB', - '1dF767C34BF222E65BD46BcC3A62de4b', - '4d44747b1D8D1648EC5713e983984a6C', - 'b89A20e9da89Eb9862502748ca9aa0bf', - 'F24dc6A1548B40dF453FdBEa4DD5CbEB', - '2Ba0BCE6-F8cC-FAFE-Ed72-f7BF01dC60A4', - '1f255AB7-d49f-f8Ff-48eD-B9c6E4674Fe2', - '8277DD2f3Fc0aaE89D601400E86E4E86', - '3Be6b8FE-8B32-240d-9C0f-0c074baC5EBb', - '94c4bBCB31D0dfa37e955Dc94eAd3Dc0', - '2B0725BE87671E4DffE19F0f7fc8aFe7' - ], - name: 'nisi ut consequat ad' - }, - { - created_at: '2019-02-18T23:00:00.0Z', - id: '4f5B88dBe1D4eB732B388abc1Baa4BAc', - updated_at: '1960-11-06T23:00:00.0Z', - org_id: 'nostrud in deserunt', - account: 'ullamco dolore pariatur sint', - host_ids: [ - '5f20569C-9E96-C794-887c-1Cb5D6e220b2', - 'D34e68beFc2d4fF1C19CF4CaF913d1b6', - '76D3AcBB-5B4C-8BB8-f3BB-69808CcFa84A', - '185dC76d-D6B8-059b-1bCb-685D2edd0CEa', - '50E0cAaDCace55e07AebC7Bcc40cfdAD', - 'be8e0A09AdEE9a23fe65972F43D31bDA', - '8B6764001Bf6d0bA4E6aEdCAf207bf71', - '80CffFbd-04C9-cBB6-5Bf5-Dc0eBbDBba70', - '55d8323C-dEbC-B10e-f88e-23ba37068C9f', - 'C5A8Fd55a0B6fa3A3a748cb0a4cc20BB', - '1dF767C34BF222E65BD46BcC3A62de4b', - '4d44747b1D8D1648EC5713e983984a6C', - 'b89A20e9da89Eb9862502748ca9aa0bf', - 'F24dc6A1548B40dF453FdBEa4DD5CbEB', - '2Ba0BCE6-F8cC-FAFE-Ed72-f7BF01dC60A4', - '1f255AB7-d49f-f8Ff-48eD-B9c6E4674Fe2', - '8277DD2f3Fc0aaE89D601400E86E4E86', - '3Be6b8FE-8B32-240d-9C0f-0c074baC5EBb', - '94c4bBCB31D0dfa37e955Dc94eAd3Dc0', - '2B0725BE87671E4DffE19F0f7fc8aFe7' - ], - name: 'nisi ut consequat ad1' - }, - { - created_at: '2019-02-18T23:00:00.0Z', - id: '4f5B88dBe1D4eB732B388abc1Baa4BAc', - updated_at: '1960-11-06T23:00:00.0Z', - org_id: 'nostrud in deserunt', - account: 'ullamco dolore pariatur sint', - host_ids: [ - '5f20569C-9E96-C794-887c-1Cb5D6e220b2', - 'D34e68beFc2d4fF1C19CF4CaF913d1b6', - '76D3AcBB-5B4C-8BB8-f3BB-69808CcFa84A', - '185dC76d-D6B8-059b-1bCb-685D2edd0CEa', - '50E0cAaDCace55e07AebC7Bcc40cfdAD', - 'be8e0A09AdEE9a23fe65972F43D31bDA', - '8B6764001Bf6d0bA4E6aEdCAf207bf71', - '80CffFbd-04C9-cBB6-5Bf5-Dc0eBbDBba70', - '55d8323C-dEbC-B10e-f88e-23ba37068C9f', - 'C5A8Fd55a0B6fa3A3a748cb0a4cc20BB', - '1dF767C34BF222E65BD46BcC3A62de4b', - '4d44747b1D8D1648EC5713e983984a6C', - 'b89A20e9da89Eb9862502748ca9aa0bf', - 'F24dc6A1548B40dF453FdBEa4DD5CbEB', - '2Ba0BCE6-F8cC-FAFE-Ed72-f7BF01dC60A4', - '1f255AB7-d49f-f8Ff-48eD-B9c6E4674Fe2', - '8277DD2f3Fc0aaE89D601400E86E4E86', - '3Be6b8FE-8B32-240d-9C0f-0c074baC5EBb', - '94c4bBCB31D0dfa37e955Dc94eAd3Dc0', - '2B0725BE87671E4DffE19F0f7fc8aFe7' - ], - name: 'nisi ut consequat ad2' - } - ], - total: 100 - } - } - })} - > - {children} - </Provider> - ); - - it('should match snapshot', () => { - const { result } = renderHook(useGroupFilter, { wrapper }); - expect(result.current).toMatchSnapshot(); - }); - - it('should return correct chips array, current value and value setter', () => { - const { result } = renderHook(useGroupFilter, { wrapper }); - const [, chips, value, setValue] = result.current; - expect(chips.length).toBe(0); - expect(value.length).toBe(0); - act(() => { - setValue(['nisi ut consequat ad1']); - }); - const [, chipsUpdated, valueUpdated] = result.current; - expect(chipsUpdated.length).toBe(1); - expect(valueUpdated).toEqual(['nisi ut consequat ad1']); - expect(chipsUpdated).toMatchSnapshot(); - }); - - it('should return empty state value if no groups obtained', () => { - const wrapper = ({ children }) => ( - <Provider - store={mockStore({ - groups: { - data: { - page: 1, - count: 50, - results: [], - total: 100 - } - } - })} - > - {children} - </Provider> - ); - const { result } = renderHook(useGroupFilter, { wrapper }); - expect(result.current).toMatchSnapshot(); - }); - }); -}); diff --git a/src/components/filters/useLastSeenFilter.js b/src/components/filters/useLastSeenFilter.js deleted file mode 100644 index 353f3fd55..000000000 --- a/src/components/filters/useLastSeenFilter.js +++ /dev/null @@ -1,137 +0,0 @@ -import { useState } from 'react'; -import { LAST_SEEN_CHIP, lastSeenItems } from '../../Utilities/constants'; - -export const lastSeenFilterState = { lastSeenFilter: [] }; -export const LAST_SEEN_FILTER = 'LAST_SEEN_FILTER'; -export const lastSeenFilterReducer = (_state, { type, payload }) => ({ - ...(type === LAST_SEEN_FILTER && { - lastSeenFilter: payload - }) -}); - -export const useLastSeenFilter = ( - [state, dispatch] = [lastSeenFilterState] -) => { - let [lastSeenStateValue, setLastSeenValue] = useState({}); - const lastSeenValue = dispatch ? state.lastSeenFilter : [lastSeenStateValue]; - const setValue = dispatch - ? (newValue) => dispatch({ type: LAST_SEEN_FILTER, payload: newValue }) - : setLastSeenValue; - - const filter = { - label: 'Last seen', - value: 'last_seen', - type: 'radio', - filterValues: { - value: lastSeenValue, - onChange: (_e, value) => setValue(value), - items: lastSeenItems - } - }; - - const chip = - !Array.isArray(lastSeenValue) && lastSeenValue !== undefined - ? [ - { - category: 'Last seen', - type: LAST_SEEN_CHIP, - chips: lastSeenItems - .filter(({ value }) => value?.mark === lastSeenValue?.mark) - .map(({ label, ...props }) => ({ name: label, ...props })) - } - ] - : []; - - const [startDate, setStartDate] = useState(); - const [endDate, setEndDate] = useState(); - const todaysDate = new Date(); - - const manageStartDate = (apiStartDate, apiEndDate)=> { - if (isNaN(apiEndDate) && isNaN(apiStartDate)) { - setValue({ ...lastSeenValue, updatedStart: null, updatedEnd: null }); - } else if (apiStartDate > apiEndDate || isNaN(apiStartDate) || apiStartDate > todaysDate) { - setValue({ ...lastSeenValue, updatedStart: null, updatedEnd: apiEndDate.toISOString() }); - } else { - setValue({ ...lastSeenValue, updatedStart: apiStartDate.toISOString() }); - } - }; - - const manageEndDate = (apiStartDate, apiEndDate)=> { - if (isNaN(apiEndDate) && isNaN(apiStartDate)) { - setValue({ ...lastSeenValue, updatedStart: null, updatedEnd: null }); - } else if (apiStartDate > apiEndDate || isNaN(apiEndDate)) { - setValue({ ...lastSeenValue, updatedStart: apiStartDate.toISOString(), updatedEnd: null }); - } else { - setValue({ ...lastSeenValue, updatedEnd: apiEndDate.toISOString() }); - } - }; - - const toValidator = (date) => { - const newDate = new Date(date); - const minDate = new Date(startDate); - - if (minDate >= newDate) { - return 'Start date must be earlier than End date.'; - } else if (newDate > todaysDate) { - return `Date must be ${todaysDate.toISOString().split('T')[0]} or earlier`; - } else { - return ''; - } - }; - - const fromValidator = (date) => { - const minDate = new Date(1950, 1, 1); - const maxDate = new Date(endDate); - - if (date < minDate) { - return 'Date is before the allowable range.'; - } else if (date > maxDate) { - return `End date must be later than Start date.`; - } else if (date > todaysDate) { - return ' Start date must be earlier than End date.'; - } else { - return ''; - } - }; - - const onFromChange = (date) => { - const newToDate = new Date(endDate); - if (date > newToDate) { - setStartDate(); - return 'End date must be later than Start date.'; - } - - setStartDate(date); - const apiStartDate = new Date(date); - apiStartDate.setUTCHours(0); - manageStartDate(apiStartDate, newToDate); - }; - - const onToChange = (date) => { - if (startDate > new Date(date)) { - return 'Start date must be earlier than End date.'; - } else if (new Date(date) > todaysDate) { - return 'End date must be later than Start date.'; - } else { - setEndDate(date); - const apiEndDate = new Date(date); - apiEndDate.setUTCHours(23, 59); - manageEndDate(new Date(startDate), apiEndDate); - } - }; - - return [ - filter, - chip, - lastSeenValue, - setValue, - toValidator, - onFromChange, - onToChange, - endDate, - startDate, - fromValidator, - setStartDate, - setEndDate - ]; -}; diff --git a/src/components/filters/useTextFilter.js b/src/components/filters/useTextFilter.js index f5dbb8558..ca71d4e3e 100644 --- a/src/components/filters/useTextFilter.js +++ b/src/components/filters/useTextFilter.js @@ -23,7 +23,7 @@ export const useTextFilter = ([state, dispatch] = [textFilterState]) => { onChange: (_e, value) => setValue(value) } }; - const chip = value?.length > 0 ? [{ + const chip = value.length > 0 ? [{ category: 'Display name', type: TEXTUAL_CHIP, chips: [ diff --git a/src/constants.js b/src/constants.js index adfa8c648..3970ee0a5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import { RHCD_FILTER_KEY, UPDATE_METHOD_KEY, HOST_GROUP_CHIP } from './Utilities/constants'; +import { RHCD_FILTER_KEY, UPDATE_METHOD_KEY } from './Utilities/constants'; export const tagsMapper = (acc, curr) => { let [namespace, keyValue] = curr.split('/'); @@ -65,6 +65,7 @@ export const generateFilters = (cells = [], filters = [], activeFilters = {}, on filters.map((filter, key) => { const activeKey = filter.index || key; const activeLabel = cells[activeKey] && (cells[activeKey].title || cells[activeKey]); + return ({ value: String(activeKey), label: activeLabel, @@ -122,12 +123,9 @@ export const getSearchParams = () => { const operatingSystem = searchParams.getAll('operating_system'); const rhcdFilter = searchParams.getAll(RHCD_FILTER_KEY); const updateMethodFilter = searchParams.getAll(UPDATE_METHOD_KEY); - const groupHostsFilter = searchParams.getAll(HOST_GROUP_CHIP); const page = searchParams.getAll('page'); const perPage = searchParams.getAll('per_page'); - const lastSeenFilter = searchParams.getAll('last_seen'); - return { status, source, tagsFilter, filterbyName, operatingSystem, rhcdFilter, updateMethodFilter, lastSeenFilter, - page, perPage, groupHostsFilter }; + return { status, source, tagsFilter, filterbyName, operatingSystem, rhcdFilter, updateMethodFilter, page, perPage }; }; export const TABLE_DEFAULT_PAGINATION = 50; // from UX table audit diff --git a/src/routes/InventoryTable.js b/src/routes/InventoryTable.js index 5df03787f..5f6f2e3ba 100644 --- a/src/routes/InventoryTable.js +++ b/src/routes/InventoryTable.js @@ -14,8 +14,6 @@ import flatMap from 'lodash/flatMap'; import { useWritePermissions, RHCD_FILTER_KEY, UPDATE_METHOD_KEY, generateFilter } from '../Utilities/constants'; import { InventoryTable as InventoryTableCmp } from '../components/InventoryTable'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; -import AddHostToGroupModal from '../components/InventoryGroups/Modals/AddHostToGroupModal'; -import useFeatureFlag from '../Utilities/useFeatureFlag'; const reloadWrapper = (event, callback) => { event.payload.then(callback); @@ -50,13 +48,8 @@ const filterMapper = { flatMap(tagFilters, mapTags) ), rhcdFilter: ({ rhcdFilter }, searchParams) => rhcdFilter?.forEach(item => searchParams.append(RHCD_FILTER_KEY, item)), - lastSeenFilter: ({ lastSeenFilter }, searchParams) => - Object.keys(lastSeenFilter || {})?.forEach(item => item === 'mark' && - searchParams.append('last_seen', lastSeenFilter[item])), updateMethodFilter: ({ updateMethodFilter }, searchParams) => - updateMethodFilter?.forEach(item => searchParams.append(UPDATE_METHOD_KEY, item)), - groupHostFilter: ({ groupHostFilter }, searchParams) => groupHostFilter - ?.forEach(item => searchParams.append('host_group', item)) + updateMethodFilter?.forEach(item => searchParams.append(UPDATE_METHOD_KEY, item)) }; const calculateFilters = (searchParams, filters = []) => { @@ -65,6 +58,7 @@ const calculateFilters = (searchParams, filters = []) => { filterMapper?.[key]?.(filter, searchParams); }); }); + return searchParams; }; @@ -84,12 +78,10 @@ const Inventory = ({ operatingSystem, rhcdFilter, updateMethodFilter, - lastSeenFilter, page, perPage, initialLoading, - hasAccess, - groupHostsFilter + hasAccess }) => { const history = useHistory(); const chrome = useChrome(); @@ -97,28 +89,18 @@ const Inventory = ({ const [isModalOpen, handleModalToggle] = useState(false); const [currentSytem, activateSystem] = useState({}); const [filters, onSetfilters] = useState( - generateFilter( - status, - source, - tagsFilter, - filterbyName, - operatingSystem, - rhcdFilter, - updateMethodFilter, - groupHostsFilter, - lastSeenFilter) + generateFilter(status, source, tagsFilter, filterbyName, operatingSystem, rhcdFilter, updateMethodFilter) ); const [ediOpen, onEditOpen] = useState(false); - const [addHostGroupModalOpen, setAddHostGroupModalOpen] = useState(false); const [globalFilter, setGlobalFilter] = useState(); const writePermissions = useWritePermissions(); const rows = useSelector(({ entities }) => entities?.rows, shallowEqual); const loaded = useSelector(({ entities }) => entities?.loaded); const selected = useSelector(({ entities }) => entities?.selected); const dispatch = useDispatch(); - const groupsEnabled = useFeatureFlag('hbi.ui.inventory-groups'); const onSelectRows = (id, isSelected) => dispatch(actions.selectEntity(id, isSelected)); + const onRefresh = (options, callback) => { onSetfilters(options?.filters); const searchParams = new URLSearchParams(); @@ -167,61 +149,10 @@ const Inventory = ({ Array.isArray(perPage) ? perPage[0] : perPage )); } - - return () => { - dispatch(actions.clearEntitiesAction()); - }; }, []); const calculateSelected = () => selected ? selected.size : 0; - //This wrapping of table actions allows to pass feature flag status and receive a prepared array of actions - const tableActions = (groupsUiStatus, row) => { - const isGroupPresentForThisRow = (row) => { - return row && row?.groups?.title !== ''; - }; - - const standardActions = [ - { - title: 'Edit', - onClick: (_event, _index, data) => { - activateSystem(() => data); - onEditOpen(() => true); - } - }, - { - title: 'Delete', - onClick: (_event, _index, { id: systemId, display_name: displayName }) => { - activateSystem(() => ({ - id: systemId, - displayName - })); - handleModalToggle(() => true); - } - } - ]; - - const actionsBehindFeatureFlag = [ - { - title: 'Add to group', - onClick: (_event, _index, { id: systemId, display_name: displayName, group_name: groupName }) => { - activateSystem(() => ({ - id: systemId, - name: displayName, - groupName - })); - setAddHostGroupModalOpen(true); - } - }, - { - title: 'Remove from group', - isDisabled: isGroupPresentForThisRow(row) - } - ]; - - return [...(groupsUiStatus ? actionsBehindFeatureFlag : []), ...standardActions]; - }; - return ( <React.Fragment> <PageHeader className="pf-m-light"> @@ -240,12 +171,26 @@ const Inventory = ({ onRefresh={onRefresh} hasCheckbox={writePermissions} autoRefresh - ignoreRefresh initialLoading={initialLoading} - tableProps={ - (writePermissions && { - actionResolver: (row) => tableActions(groupsEnabled, row), canSelectAll: false })} {...(writePermissions && { + actions: [ + { + title: 'Delete', + onClick: (_event, _index, { id: systemId, display_name: displayName }) => { + activateSystem(() => ({ + id: systemId, + displayName + })); + handleModalToggle(() => true); + } + }, { + title: 'Edit', + onClick: (_event, _index, data) => { + activateSystem(() => data); + onEditOpen(() => true); + } + } + ], actionsConfig: { actions: [{ label: 'Delete', @@ -282,13 +227,15 @@ const Inventory = ({ } } })} + tableProps={{ + canSelectAll: false + }} onRowClick={(_e, id, app) => history.push(`/${id}${app ? `/${app}` : ''}`)} /> </GridItem> </Grid> </Main> <DeleteModal - className ='sentry-mask data-hj-suppress' handleModalToggle={handleModalToggle} isModalOpen={isModalOpen} currentSytems={currentSytem} @@ -316,6 +263,7 @@ const Inventory = ({ handleModalToggle(false); }} /> + <TextInputModal title="Edit display name" isOpen={ediOpen} @@ -326,13 +274,6 @@ const Inventory = ({ onEditOpen(false); }} /> - <AddHostToGroupModal - isModalOpen={addHostGroupModalOpen} - setIsModalOpen={setAddHostGroupModalOpen} - modalState={currentSytem} - //should be replaced with a fetch to update the values in the table - reloadData={() => console.log('data reloaded')} - /> </React.Fragment> ); }; @@ -348,9 +289,7 @@ Inventory.propTypes = { initialLoading: PropTypes.bool, rhcdFilter: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.string]), updateMethodFilter: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.string]), - hasAccess: PropTypes.bool, - groupHostsFilter: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.string]), - lastSeenFilter: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.string]) + hasAccess: PropTypes.bool }; Inventory.defaultProps = { diff --git a/src/routes/InventoryTable.test.js b/src/routes/InventoryTable.test.js index ea7f6ca7f..ad3166c63 100644 --- a/src/routes/InventoryTable.test.js +++ b/src/routes/InventoryTable.test.js @@ -27,6 +27,15 @@ jest.mock('@redhat-cloud-services/frontend-components-utilities/RBACHook', () => usePermissionsWithContext: () => ({ hasAccess: true }) })); +jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({ + __esModule: true, + default: () => ({ + updateDocumentTitle: jest.fn(), + appAction: jest.fn(), + appObjectId: jest.fn(), + on: jest.fn() + }) +})); jest.mock('../Utilities/useFeatureFlag'); describe('InventoryTable', () => { @@ -249,12 +258,8 @@ describe('InventoryTable', () => { expect(wrapper.find('DropdownMenu')).toHaveLength(1); await act(async () => { - const dropdownItems = wrapper.find('DropdownItem'); - - const deleteDropdown = dropdownItems.at(1); - deleteDropdown.find('button').simulate('click'); + wrapper.find('DropdownItem').first().find('button').simulate('click'); }); - wrapper.update(); expect(wrapper.find(DeleteModal).props().isModalOpen).toEqual(true); diff --git a/src/store/action-types.js b/src/store/action-types.js index 779b1cceb..a3911afcf 100644 --- a/src/store/action-types.js +++ b/src/store/action-types.js @@ -67,4 +67,3 @@ export const CLEAR_FILTERS = 'CLEAR_FILTERS'; export const TOGGLE_TAG_MODAL = 'TOGGLE_TAG_MODAL'; export const CONFIG_CHANGED = 'CONFIG_CHANGED'; export const TOGGLE_DRAWER = 'TOGGLE_INVENTORY_DRAWER'; -export const CLEAR_ENTITIES = 'CLEAR_ENTITIES'; diff --git a/src/store/actions.js b/src/store/actions.js index 202699fbe..0167d550b 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -1,5 +1,4 @@ -import { ACTION_TYPES, CLEAR_NOTIFICATIONS, SET_INVENTORY_FILTER, SET_PAGINATION, - CLEAR_ENTITIES } from './action-types'; +import { ACTION_TYPES, CLEAR_NOTIFICATIONS, SET_INVENTORY_FILTER, SET_PAGINATION } from './action-types'; import { hosts, getEntitySystemProfile } from '../api'; export * from './system-issues-actions'; export * from './inventory-actions'; @@ -78,8 +77,3 @@ export const editAnsibleHost = (id, value, origValue) => ({ } } }); - -export const clearEntitiesAction = () => ({ - type: CLEAR_ENTITIES, - payload: [] -}); diff --git a/src/store/actions.test.js b/src/store/actions.test.js index ff71dc4a0..e3bf3a6d0 100644 --- a/src/store/actions.test.js +++ b/src/store/actions.test.js @@ -2,7 +2,7 @@ import { editAnsibleHost, editDisplayName, fetchGroups, systemProfile } from './actions'; import { hosts } from '../api'; import mockedData from '../__mocks__/mockedData.json'; -import fixturesGroups from '../../cypress/fixtures/groups.json'; +import mockedGroups from '../__mocks__/mockedGroups.json'; import MockAdapter from 'axios-mock-adapter'; const mocked = new MockAdapter(hosts.axios); @@ -65,10 +65,10 @@ describe('editAnsibleHost', () => { describe('fetchGroups', () => { it('should call correct endpoint', async () => { mocked.onGet(new RegExp('/api/inventory/v1/groups*')).reply(() => { - return [200, fixturesGroups]; + return [200, mockedGroups]; }); const { type, payload } = await fetchGroups(); expect(type).toBe('GROUPS'); - expect(await payload).toEqual(fixturesGroups); + expect(await payload).toEqual(mockedGroups); }); }); diff --git a/src/store/entities.js b/src/store/entities.js index 8e16d9f2d..c90947e26 100644 --- a/src/store/entities.js +++ b/src/store/entities.js @@ -9,8 +9,7 @@ import { ENTITIES_LOADING, CLEAR_FILTERS, TOGGLE_TAG_MODAL, - CONFIG_CHANGED, - CLEAR_ENTITIES + CONFIG_CHANGED } from './action-types'; import { mergeArraysByKey } from '@redhat-cloud-services/frontend-components-utilities/helpers'; import { DateFormat } from '@redhat-cloud-services/frontend-components/DateFormat'; @@ -22,8 +21,6 @@ import InsightsDisconnected from '../Utilities/InsightsDisconnected'; import OperatingSystemFormatter from '../Utilities/OperatingSystemFormatter'; import { Tooltip } from '@patternfly/react-core'; import { verifyCulledInsightsClient } from '../Utilities/sharedFunctions'; -import { fitContent } from '@patternfly/react-table'; -import isEmpty from 'lodash/isEmpty'; export const defaultState = { loaded: false, @@ -49,11 +46,9 @@ export const defaultColumns = (groupsEnabled = false) => ([ ...(groupsEnabled ? [{ key: 'groups', sortKey: 'groups', - title: 'Group', + title: 'Groups', props: { width: 10 }, - // eslint-disable-next-line camelcase - renderFunc: (value, systemId, { group_name }) => isEmpty(group_name) ? 'N/A' : group_name, - transforms: [fitContent] + renderFunc: () => <React.Fragment>N/A</React.Fragment> }] : []), { key: 'tags', @@ -99,8 +94,7 @@ export const defaultColumns = (groupsEnabled = false) => ([ } > <DateFormat date={ value } /> </CullingInformation> : new Date(value).toLocaleString(); }, - props: { width: 10 }, - transforms: [fitContent] + props: { width: 10 } } ]); @@ -124,10 +118,6 @@ function clearFilters(state) { }; } -const clearEntities = () => { - return defaultState; -}; - // eslint-disable-next-line camelcase function entitiesLoaded(state, { payload: { results, per_page: perPage, page, count, total, loaded, filters }, meta }) { // Older requests should not rewrite the state @@ -180,7 +170,7 @@ function selectEntity(state, { payload }) { function versionsLoaded(state, { payload: { results } }) { return { ...state, - operatingSystems: (results || []).map(entry => { + operatingSystems: results.map(entry => { const { name, major, minor } = entry.value; const versionStringified = `${major}.${minor}`; return { label: `${name} ${versionStringified}`, value: versionStringified }; @@ -310,6 +300,5 @@ export default { [CLEAR_FILTERS]: clearFilters, [ENTITIES_LOADING]: (state, { payload: { isLoading } }) => ({ ...state, loaded: !isLoading }), [TOGGLE_TAG_MODAL]: toggleTagModalReducer, - [CONFIG_CHANGED]: (state, { payload }) => ({ ...state, invConfig: payload }), - [CLEAR_ENTITIES]: clearEntities + [CONFIG_CHANGED]: (state, { payload }) => ({ ...state, invConfig: payload }) }; diff --git a/src/store/inventory-actions.js b/src/store/inventory-actions.js index 3c75b91d7..57052c366 100644 --- a/src/store/inventory-actions.js +++ b/src/store/inventory-actions.js @@ -37,14 +37,11 @@ export const loadEntities = (items = [], { filters, ...config }, { showTags } = ...filters.length === 0 && { registeredWithFilter: [] }, ...(isFilterDisabled('stale') && { staleFilter: undefined }), ...(isFilterDisabled('registeredWith') && { registeredWithFilter: undefined }), - ...(isFilterDisabled('operating_system') && { osFilter: undefined }), - ...(isFilterDisabled('host_group')) && { groupHostFilter: undefined } + ...(isFilterDisabled('operating_system') && { osFilter: undefined }) }) : { ...(isFilterDisabled('stale') && { staleFilter: undefined }), - ...(isFilterDisabled('last_seen') && { lastSeenFilter: undefined }), ...(isFilterDisabled('registeredWith') && { registeredWithFilter: undefined }), - ...(isFilterDisabled('operating_system') && { osFilter: undefined }), - ...(isFilterDisabled('host_group')) && { groupHostFilter: undefined } + ...(isFilterDisabled('operating_system') && { osFilter: undefined }) }; const orderBy = config.orderBy || 'updated';