From d0d50d340b7ec366ef5e1fef635bf0fb2588b5a0 Mon Sep 17 00:00:00 2001 From: Katia Aresti Date: Tue, 3 Oct 2023 15:23:35 +0200 Subject: [PATCH] ISPN-15125 Error page is shown for non-admin user if accesses cache to which has permission --- .eslintrc | 1 + .github/workflows/pull_requests.yml | 2 +- README.md | 10 +- cypress/e2e/cache-detail-search.cy.js | 80 +++++ cypress/e2e/cache-detail.cy.js | 289 +++--------------- cypress/e2e/cache-metrics.cy.js | 25 +- cypress/e2e/data-container.cy.js | 5 +- cypress/e2e/global-stats.cy.js | 6 +- cypress/e2e/rbac_func.cy.js | 27 +- run-server-for-e2e-container.sh | 17 ++ run-server-for-e2e.sh | 144 --------- .../services/cacheConfigUtils.test.ts | 42 ++- src/app/CacheManagers/CacheTableDisplay.tsx | 17 +- src/app/Caches/CacheMetrics.tsx | 41 +-- src/app/Caches/DataDistributionChart.tsx | 5 +- src/app/Caches/DetailCache.tsx | 90 +++--- src/app/Caches/Query/QueryEntries.tsx | 4 +- src/app/assets/languages/en.json | 19 +- src/services/cacheConfigUtils.ts | 55 +++- src/services/cacheService.ts | 33 +- src/types/InfinispanTypes.ts | 10 +- 21 files changed, 401 insertions(+), 521 deletions(-) create mode 100644 cypress/e2e/cache-detail-search.cy.js create mode 100755 run-server-for-e2e-container.sh delete mode 100755 run-server-for-e2e.sh diff --git a/.eslintrc b/.eslintrc index f3a8b5194..80a6a65c7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -41,6 +41,7 @@ "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/no-empty-function": "off", "prettier/prettier": "off", "import/no-unresolved": "off", "import/extensions": "off", diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml index 65e28f4e6..f86ebcea4 100644 --- a/.github/workflows/pull_requests.yml +++ b/.github/workflows/pull_requests.yml @@ -51,7 +51,7 @@ jobs: docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.ow2.asm:asm-tree:9.4 docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.ow2.asm:asm-util:9.4 - - name: Restarting Infnispan Server After Nashorn Installation + - name: Restarting Infinispan Server After Nashorn Installation run: | docker restart $(docker ps -q -l) # Wait for server to startup diff --git a/README.md b/README.md index a55c0b399..5c33deecb 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,13 @@ Unit test run by default. To skip them use 'skipTests' property. #### Run Cypress IT Tests Integration tests don't run by default locally. They always run in CI. -To run integration tests locally: -You need to run first `./run-server-for-e2e.sh` that will download and run the infinispan server. -Then `mvn clean install -De2e=true` +To run integration tests locally, you need Docker: + +```shell +npm run build # build the console +./run-server-for-e2e-container.sh # will run the latest image of Infinispan for this branch version and the built console +npm run cy:e2e # will run cypress locally +``` ## Configurations * [TypeScript Config](./tsconfig.json) diff --git a/cypress/e2e/cache-detail-search.cy.js b/cypress/e2e/cache-detail-search.cy.js new file mode 100644 index 000000000..b850c9451 --- /dev/null +++ b/cypress/e2e/cache-detail-search.cy.js @@ -0,0 +1,80 @@ +const { CandyCaneIcon } = require('@patternfly/react-icons'); + +describe('Cache Detail Overview', () => { + beforeEach(() => { + cy.login(Cypress.env('username'), Cypress.env('password'), '/cache/people'); + }); + + it('successfully searches by values', () => { + //Opening indexed-cache cache page. + cy.login(Cypress.env('username'), Cypress.env('password'), '/cache/indexed-cache'); + + // Going back to cache entries page + cy.get('[data-cy=cacheEntriesTab]').click(); + cy.get('[data-cy=queriesTab]').click(); + cy.get('#textSearchByQuery').click().type('from org.infinispan.Person where age>2'); + cy.get('[data-cy=searchButton]').click(); + cy.contains('1 - 1 of 1'); + cy.contains('Elaia'); + + // Going back to cache entries page + cy.get('[data-cy=cacheEntriesTab]').click(); + cy.get('[data-cy=queriesTab]').click(); + + cy.get('#textSearchByQuery').click().clear().type("from org.infinispan.Person where name = 'Elaia'"); + cy.get('[data-cy=searchButton]').click(); + cy.contains('1 - 1 of 1'); + cy.contains('Elaia'); + + // Going back to cache entries page + cy.get('[data-cy=cacheEntriesTab]').click(); + cy.get('[data-cy=queriesTab]').click(); + + cy.get('#textSearchByQuery').click().clear().type('from org.infinispan.Person where age=2'); + cy.get('[data-cy=searchButton]').click(); + cy.contains('Values not found.'); + + cy.get('[data-cy=viewQueryMetricsButton]').click(); + //Verify query metrics available + cy.contains('Query metrics'); + cy.contains('from org.infinispan.Person'); + cy.get('[data-cy=clearQueryMetricsButton]').click(); + cy.get('[data-cy=confirmButton]').click(); + cy.contains('from org.infinispan.Person').should('not.exist'); + }); + + it('successfully manages indexes', () => { + //Opening indexed-cache cache page. + cy.login(Cypress.env("username"), Cypress.env("password"), '/cache/indexed-cache'); + + cy.get("[data-cy=manageIndexesLink]").click(); + cy.contains("org.infinispan.Person"); + cy.contains("3 k"); + cy.get("[data-cy=backButton]").click(); + cy.contains("Elaia"); + + cy.get("[data-cy=manageIndexesLink]").click(); + cy.get("[data-cy=clearIndexButton]").click(); + cy.contains("Permanently clear index?"); + cy.get("[data-cy=cancelButton]").click(); + cy.contains("Permanently clear index?").should("not.exist"); + + cy.get("[data-cy=clearIndexButton]").click(); + cy.get("[data-cy=clearIndex]").click(); + cy.contains("Index of cache indexed-cache cleared."); + cy.contains("0"); + + cy.get("[data-cy=rebuildIndexButton]").click(); + cy.contains("Rebuild index?"); + cy.get("[data-cy=cancelReindexButton]").click(); + + cy.get("[data-cy=rebuildIndexButton]").click(); + cy.get("[data-cy=reindexButton]").click(); + cy.contains("1").should("not.exist"); + + cy.get("[data-cy=backButton]").click(); + cy.contains("Elaia"); + cy.get("[data-cy=manageIndexesLink]").click(); + cy.contains("3 k"); + }) +}); diff --git a/cypress/e2e/cache-detail.cy.js b/cypress/e2e/cache-detail.cy.js index 761c570bd..953b197a2 100644 --- a/cypress/e2e/cache-detail.cy.js +++ b/cypress/e2e/cache-detail.cy.js @@ -15,8 +15,6 @@ describe('Cache Detail Overview', () => { cy.contains('Entries'); cy.contains('Configuration'); cy.contains('Metrics (Enabled)'); - - verifyCacheMetrics(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }); it('successfully adds new entry', () => { @@ -47,43 +45,6 @@ describe('Cache Detail Overview', () => { cy.get('.pf-c-alert__action > .pf-c-button').click(); //Closing alert popup. cy.contains('org.infinispan.Person'); - //Adding int32 key/value - cy.get('[data-cy=addEntryButton]').click(); - cy.get('#keyContentType').click(); - cy.get('#int32').click(); - cy.get('#key-entry').click().type(2147483648); //Adding greater value than int32 max - cy.get('#value-entry').click().type('test'); - cy.get('[data-cy=addButton]').click(); - cy.contains('Unexpected error'); - //Changing to valid number - cy.get('#key-entry').click().clear().type(2147483647); - cy.get('#valueContentType').click(); - cy.get('#int32').click(); - cy.get('#value-entry').click().clear().type(-2147483647); - cy.get('[data-cy=addButton]').click(); - cy.contains('Entry added to cache people.'); - cy.get('.pf-c-alert__action > .pf-c-button').click(); //Closing alert popup. - cy.contains('2147483647'); - cy.contains('-2147483647'); - - //Adding int64 key/value - cy.get('[data-cy=addEntryButton]').click(); - cy.get('#keyContentType').click(); - cy.get('#int64').click(); - cy.get('#key-entry').click().type(9223372036854775808); //Adding greater value than int64 max - cy.get('#value-entry').click().type('test'); - cy.get('[data-cy=addButton]').click(); - cy.contains('Unexpected error'); - //Changing to valid number - cy.get('#key-entry').click().clear().type('9223372036854775807'); - cy.get('#valueContentType').click(); - cy.get('#int64').click(); - cy.get('#value-entry').click().clear().type('-9223372036854775807'); - cy.get('[data-cy=addButton]').click(); - cy.get('.pf-c-alert__action > .pf-c-button').click(); //Closing alert popup. - //cy.contains('9223372036854775807'); @TODO Uncomment when ISPN-14062 is fixed. - //cy.contains('-9223372036854775807'); - //Adding double key/value cy.get('[data-cy=addEntryButton]').click(); cy.get('#cacheName').should('be.disabled'); @@ -111,8 +72,8 @@ describe('Cache Detail Overview', () => { cy.get('[data-cy=addButton]').click(); cy.contains('Entry added to cache people.'); cy.get('.pf-c-alert__action > .pf-c-button').click(); //Closing alert popup. - //cy.contains('3.402823466e+38'); @TODO Uncomment when ISPN-14062 is fixed. - //cy.contains('3.402823466e-38'); + cy.contains('3.4028235e+38'); + cy.contains('3.4028235e-38'); //Adding boolean key/value cy.get('[data-cy=addEntryButton]').click(); @@ -144,33 +105,24 @@ describe('Cache Detail Overview', () => { cy.contains('00110010101001011001101110010101'); cy.contains('00110010101001011001101110010101'); - //Adding uint32 key/value @TODO uncomment when ISPN-14093 is fixed. - // cy.get('[data-cy=addEntryButton]').click(); - // cy.get('#cacheName').should('be.disabled'); - // cy.get('#keyContentType').click(); - // cy.get('#uint32').click(); - // cy.get('#key-entry').click().type('4294967295'); - // cy.get('#valueContentType').click(); - // cy.get('#uint32').click(); - // cy.get('#value-entry').click().type('4294967295'); - // cy.get('[data-cy=addButton]').click(); - // cy.contains('Entry added to cache people.'); - // cy.contains('4294967295'); - // cy.contains('4294967295'); - - //Adding uint64 key/value @TODO uncomment when ISPN-14093 is fixed. - // cy.get('[data-cy=addEntryButton]').click(); - // cy.get('#cacheName').should('be.disabled'); - // cy.get('#keyContentType').click(); - // cy.get('#uint64').click(); - // cy.get('#key-entry').click().type('9223372036854775807'); - // cy.get('#valueContentType').click(); - // cy.get('#uint364').click(); - // cy.get('#value-entry').click().type('9223372036854775807'); - // cy.get('[data-cy=addButton]').click(); - // cy.contains('Entry added to cache people.'); - // cy.contains('9223372036854775807'); - // cy.contains('9223372036854775807'); + //Adding int32 key/value + cy.get('[data-cy=addEntryButton]').click(); + cy.get('#keyContentType').click(); + cy.get('#int32').click(); + cy.get('#key-entry').click().type(2147483648); //Adding greater value than int32 max + cy.get('#value-entry').click().type('test'); + cy.get('[data-cy=addButton]').click(); + cy.contains('Unexpected error'); + //Changing to valid number + cy.get('#key-entry').click().clear().type(2147483647); + cy.get('#valueContentType').click(); + cy.get('#int32').click(); + cy.get('#value-entry').click().clear().type(-2147483647); + cy.get('[data-cy=addButton]').click(); + cy.contains('Entry added to cache people.'); + cy.get('.pf-c-alert__action > .pf-c-button').click(); //Closing alert popup. + cy.contains('2147483647'); + cy.contains('-2147483647'); //Adding sint32 key/value cy.get('[data-cy=addEntryButton]').click(); @@ -186,7 +138,7 @@ describe('Cache Detail Overview', () => { cy.contains('2147483647'); cy.contains('-2147483647'); - //Adding int64 key/value + //Adding sint64 key/value cy.get('[data-cy=addEntryButton]').click(); cy.get('#keyContentType').click(); cy.get('#sint64').click(); @@ -197,10 +149,36 @@ describe('Cache Detail Overview', () => { cy.get('[data-cy=addButton]').click(); cy.contains('Entry added to cache people.'); cy.get('.pf-c-alert__action > .pf-c-button').click(); //Closing alert popup. - //cy.contains('9223372036854775807'); //@TODO Uncomment when ISPN-14062 is fixed. - //cy.contains('-9223372036854775807'); + cy.contains('9223372036854776000'); + cy.contains('-9223372036854776000'); - verifyCacheMetrics(10, 10, 10, 0, 10, 10, 10, 0, 0, 0); + //Adding uint32 key/value + cy.get('[data-cy=addEntryButton]').click(); + cy.get('#cacheName').should('be.disabled'); + cy.get('#keyContentType').click(); + cy.get('#uint32').click(); + cy.get('#key-entry').click().type('4294967295'); + cy.get('#valueContentType').click(); + cy.get('#uint32').click(); + cy.get('#value-entry').click().type('4294967295'); + cy.get('[data-cy=addButton]').click(); + cy.contains('Entry added to cache people.'); + cy.contains('4294967295'); + cy.contains('4294967295'); + + //Adding uint64 key/value + cy.get('[data-cy=addEntryButton]').click(); + cy.get('#cacheName').should('be.disabled'); + cy.get('#keyContentType').click(); + cy.get('#uint64').click(); + cy.get('#key-entry').click().type('8223372036854775807'); + cy.get('#valueContentType').click(); + cy.get('#uint32').click(); + cy.get('#value-entry').click().type('8223372036854775807'); + cy.get('[data-cy=addButton]').click(); + cy.contains('Entry added to cache people.'); + cy.contains('8223372036854775807'); + cy.contains('8223372036854775807'); }); it('successfully searches entries by key', () => { @@ -210,7 +188,6 @@ describe('Cache Detail Overview', () => { cy.contains('stringKey'); cy.contains('1 - 1 of 1'); - verifyCacheMetrics(10, 10, 10, 1, 10, 10, 11, 0, 0, 0); // Going back to cache entries page cy.get('[data-cy=cacheEntriesTab]').click(); @@ -222,7 +199,6 @@ describe('Cache Detail Overview', () => { cy.contains('stringKey').should('not.exist'); cy.contains('00110010101001011001101110010101'); cy.contains('1 - 1 of 1'); - verifyCacheMetrics(10, 10, 10, 2, 10, 10, 12, 0, 0, 0); // Going back to cache entries page cy.get('[data-cy=cacheEntriesTab]').click(); @@ -234,7 +210,6 @@ describe('Cache Detail Overview', () => { cy.contains('stringKey').should('not.exist'); cy.contains('2147483647'); cy.contains('1 - 1 of 1'); - verifyCacheMetrics(10, 10, 10, 3, 10, 10, 13, 0, 0, 0); // Going back to cache entries page cy.get('[data-cy=cacheEntriesTab]').click(); @@ -246,7 +221,6 @@ describe('Cache Detail Overview', () => { cy.contains('stringKey').should('not.exist'); cy.contains('2147483647'); cy.contains('1 - 1 of 1'); - verifyCacheMetrics(10, 10, 10, 4, 10, 10, 14, 0, 0, 0); // Going back to cache entries page cy.get('[data-cy=cacheEntriesTab]').click(); @@ -258,7 +232,6 @@ describe('Cache Detail Overview', () => { cy.contains('stringKey').should('not.exist'); cy.contains('true'); cy.contains('1 - 1 of 1'); - verifyCacheMetrics(10, 10, 10, 5, 10, 10, 15, 0, 0, 0); // Going back to cache entries page cy.get('[data-cy=cacheEntriesTab]').click(); @@ -272,7 +245,6 @@ describe('Cache Detail Overview', () => { cy.contains('true').should('not.exist'); cy.contains('Elaia'); cy.contains('1 - 1 of 1'); - verifyCacheMetrics(10, 10, 10, 6, 10, 10, 16, 0, 0, 0); }); it('successfully edits existing entry', () => { @@ -285,7 +257,6 @@ describe('Cache Detail Overview', () => { cy.get('[data-cy=addButton]').click(); cy.contains('Entry updated in cache people.'); cy.contains('changedValue'); - verifyCacheMetrics(10, 10, 10, 8, 10, 11, 18, 0, 0, 0); }); it('successfully deletes existing entry', () => { @@ -303,9 +274,6 @@ describe('Cache Detail Overview', () => { cy.get('[data-cy=deleteEntryButton]').click(); cy.contains('Entry true deleted.'); cy.contains('changedValue').should('not.exist'); - - //Verify metrics - verifyCacheMetrics(9, 9, 9, 9, 10, 11, 19, 1, 0, 0); }); it('successfully clears all entries', () => { @@ -313,100 +281,8 @@ describe('Cache Detail Overview', () => { cy.get('[data-cy=clearAllButton]').click(); cy.get('[data-cy=deleteButton]').click(); cy.contains('The cache is empty'); - - //Verify metrics - verifyCacheMetrics(0, 0, 0, 9, 10, 11, 19, 1, 0, 0); - }); - - it('successfully searches by values', () => { - //Opening indexed-cache cache page. - cy.login(Cypress.env('username'), Cypress.env('password'), '/cache/indexed-cache'); - - //Verify metrics - verifyCacheMetrics(11, 11, 11, 0, 11, 11, 11, 0, 0, 0); - - // Going back to cache entries page - cy.get('[data-cy=cacheEntriesTab]').click(); - cy.get('[data-cy=queriesTab]').click(); - cy.get('#textSearchByQuery').click().type('from org.infinispan.Person where age>2'); - cy.get('[data-cy=searchButton]').click(); - cy.contains('1 - 1 of 1'); - cy.contains('Elaia'); - - //Verify metrics - verifyCacheMetrics(11, 11, 11, 1, 11, 11, 12, 0, 0, 0); - - // Going back to cache entries page - cy.get('[data-cy=cacheEntriesTab]').click(); - cy.get('[data-cy=queriesTab]').click(); - - cy.get('#textSearchByQuery').click().clear().type("from org.infinispan.Person where name = 'Elaia'"); - cy.get('[data-cy=searchButton]').click(); - cy.contains('1 - 1 of 1'); - cy.contains('Elaia'); - - //Verify metrics - verifyCacheMetrics(11, 11, 11, 2, 11, 11, 13, 0, 0, 0); - - // Going back to cache entries page - cy.get('[data-cy=cacheEntriesTab]').click(); - cy.get('[data-cy=queriesTab]').click(); - - cy.get('#textSearchByQuery').click().clear().type('from org.infinispan.Person where age=2'); - cy.get('[data-cy=searchButton]').click(); - cy.contains('Values not found.'); - //Verify metrics - verifyCacheMetrics(11, 11, 11, 2, 11, 11, 13, 0, 0, 0); - - //Verify query metrics available - cy.contains('Query metrics'); - cy.contains('from org.infinispan.Person'); - cy.get('[data-cy=clearQueryMetricsButton]').click(); - cy.get('[data-cy=confirmButton]').click(); - cy.contains('from org.infinispan.Person').should('not.exist'); - - // Going back to cache entries page - cy.get('[data-cy=cacheEntriesTab]').click(); - cy.get('[data-cy="manageEntriesTab"]').click(); - cy.contains('key-9'); }); - //@TODO uncomment when ISPN-14189 is fixed. - // it('successfully manages indexes', () => { - // //Opening indexed-cache cache page. - // cy.login(Cypress.env("username"), Cypress.env("password"), '/cache/indexed-cache'); - - // cy.get("[data-cy=manageIndexesLink]").click(); - // cy.contains("org.infinispan.Person"); - // cy.contains("3 k"); - // cy.get("[data-cy=backButton]").click(); - // cy.contains("Elaia"); - - // cy.get("[data-cy=manageIndexesLink]").click(); - // cy.get("[data-cy=clearIndexButton]").click(); - // cy.contains("Permanently clear index?"); - // cy.get("[data-cy=cancelButton]").click(); - // cy.contains("Permanently clear index?").should("not.exist"); - - // cy.get("[data-cy=clearIndexButton]").click(); - // cy.get("[data-cy=clearIndex]").click(); - // cy.contains("Index of cache indexed-cache cleared."); - // cy.contains("0"); - - // cy.get("[data-cy=rebuildIndexButton]").click(); - // cy.contains("Rebuild index?"); - // cy.get("[data-cy=cancelReindexButton]").click(); - - // cy.get("[data-cy=rebuildIndexButton]").click(); - // cy.get("[data-cy=reindexButton]").click(); - // cy.contains("1").should("not.exist"); - - // cy.get("[data-cy=backButton]").click(); - // cy.contains("Elaia"); - // cy.get("[data-cy=manageIndexesLink]").click(); - // cy.contains("3 k"); - // }) - it('successfully adds new entry with expiration and waits till entry is expired', () => { //Adding sint32 key/value cy.get('[data-cy=addEntryButton]').click(); @@ -427,69 +303,4 @@ describe('Cache Detail Overview', () => { cy.contains('2147483647').should('not.exist'); cy.contains('-2147483647').should('not.exist'); }); - - function verifyCacheMetrics( - appUniqueEntries, - appEntrInMemory, - appEntries, - hitsCount, - missesCount, - storesCount, - retrievalsCount, - removeHits, - removeMisses, - evictions - ) { - cy.get('[data-cy=cacheMetricsTab]').click(); - cy.contains('Entries'); - cy.contains('Memory'); - cy.contains('Performance'); - cy.contains('Data access'); - cy.contains('Data for nodes'); - - cy.get('dt[aria-label="view-cache-approximate-unique-entries"]') - .invoke('text') - .then(parseFloat) - .should('be.eq', appUniqueEntries); - - cy.get('dt[aria-label="view-cache-approximate-entries-in-memory"]') - .invoke('text') - .then(parseFloat) - .should('be.eq', appEntrInMemory); - - cy.get('dt[aria-label="view-cache-approximate-entries"]') - .invoke('text') - .then(parseFloat) - .should('be.eq', appEntries); - - cy.get('dt[aria-label="view-cache-metrics-heap"]').invoke('text').then(parseFloat).should('be.eq', -1); - - cy.get('dt[aria-label="view-cache-metrics-nodes"]').invoke('text').then(parseFloat).should('be.eq', 1); - - // cy.get('dt[aria-label="average-cache-read-time"]') - // .invoke('text') - // .then(parseFloat) - // .should('be.eq', 0); - - // cy.get('dt[aria-label="average-cache-write-time"]') - // .invoke('text') - // .then(parseFloat) - // .should('be.eq', 0); - - // cy.get('dt[aria-label="average-cache-delete-time"]') - // .invoke('text') - // .then(parseFloat) - // .should('be.eq', 0); - - cy.contains('Hits: ' + hitsCount); - cy.contains('Misses: ' + missesCount); - cy.contains('Stores: ' + storesCount); - cy.contains('Retrievals: ' + retrievalsCount); - cy.contains('Remove hits: ' + removeHits); - cy.contains('Remove misses: ' + removeMisses); - cy.contains('Evictions: ' + evictions); - - cy.get('[data-cy=data-access-chart]').should('exist'); - cy.get('[data-cy=data-distribution-chart]').should('exist'); - } }); diff --git a/cypress/e2e/cache-metrics.cy.js b/cypress/e2e/cache-metrics.cy.js index 50fa1d1a6..f6bfa701a 100644 --- a/cypress/e2e/cache-metrics.cy.js +++ b/cypress/e2e/cache-metrics.cy.js @@ -3,7 +3,7 @@ describe('Cache Metrics Overview', () => { cy.login(Cypress.env('username'), Cypress.env('password'), '/cache/people'); //Check for Labels cy.contains('Metrics (Enabled)').click(); - cy.contains('Approximate number of entries'); + cy.contains('Approx. number of entries'); cy.contains('Minimum number of nodes'); // Check for data greater than -1 @@ -12,19 +12,38 @@ describe('Cache Metrics Overview', () => { .then(parseFloat) .should('be.gt', -1); cy.get('dt[aria-label="view-cache-metrics-nodes"]').invoke('text').then(parseFloat).should('be.gt', -1); + + cy.get('[data-cy=cacheMetricsTab]').click(); + cy.contains('Entries'); + cy.contains('Memory'); + cy.contains('Performance'); + cy.contains('Data access'); + cy.contains('Data for nodes'); + + + cy.contains('Hits: '); + cy.contains('Misses: '); + cy.contains('Stores: '); + cy.contains('Retrievals: '); + cy.contains('Remove hits: '); + cy.contains('Remove misses: '); + cy.contains('Evictions: '); + + cy.get('[data-cy=data-access-chart]').should('exist'); + cy.get('[data-cy=data-distribution-chart]').should('exist'); }); it('successfully checks cache metrics for heap memory', () => { cy.login(Cypress.env('username'), Cypress.env('password'), '/cache/heap-test'); cy.contains('Metrics (Enabled)').click(); - cy.contains('Size of cache in heap memory'); + cy.contains('Size in heap memory'); cy.get('dt[aria-label="view-cache-metrics-heap"]').invoke('text').then(parseFloat).should('be.gt', -1); }); it('successfully checks cache metrics for off-heap memory', () => { cy.login(Cypress.env('username'), Cypress.env('password'), '/cache/off-heap-test'); cy.contains('Metrics (Enabled)').click(); - cy.contains('Size of cache in off-heap memory'); + cy.contains('Size in off-heap memory'); cy.get('dt[aria-label="view-cache-metrics-off-heap"]').invoke('text').then(parseFloat).should('be.gt', -1); }); }); diff --git a/cypress/e2e/data-container.cy.js b/cypress/e2e/data-container.cy.js index 3339a1efc..5ae00c073 100644 --- a/cypress/e2e/data-container.cy.js +++ b/cypress/e2e/data-container.cy.js @@ -152,14 +152,11 @@ describe('Data Container Overview', () => { cy.get('[data-cy=cacheFilterSelect]').click(); cy.get('[data-cy=cacheFilterSelectExpanded]').should('exist'); cy.get('[id$="Invalidated"]').click(); //Filtering invalidated caches - cy.get('[id$="Scattered"]').click(); //Filtering scattered caches cy.get('[data-cy=cacheFilterSelect]').click(); //Closing filter selectbox - //Verifying that only replicated,invalidated and scattered caches are shown cy.contains('jboss-cache'); cy.contains('invalidationCache'); - cy.contains('scattered-cache'); - cy.get('[data-cy=cachesTable] tr').should('have.length', 4); //4 including header row + cy.get('[data-cy=cachesTable] tr').should('have.length', 3); //3 including header row //Clears all filters cy.get('[data-cy=clearAllButton]').click(); diff --git a/cypress/e2e/global-stats.cy.js b/cypress/e2e/global-stats.cy.js index 550b19ddf..9e58e7fe7 100644 --- a/cypress/e2e/global-stats.cy.js +++ b/cypress/e2e/global-stats.cy.js @@ -12,8 +12,7 @@ describe('Global stats', () => { cy.contains('Cluster distribution'); }); - //View all caches href - it('successfully loads Global stats', () => { + it('successfully views all caches href', () => { //click View all caches should navigate to console page cy.get('[data-cy="viewCachesLink"]').click(); //Verify that page is properly loaded after click; @@ -24,8 +23,7 @@ describe('Global stats', () => { cy.contains('java-serialized-cache'); }); - //View Cluster Status href - it('successfully loads Global stats', () => { + it('successfully views cluster status ref', () => { //click View Cluster Status should navigate to cluster-membership page cy.get('.pf-l-grid__item:nth-child(3) .pf-c-button').click(); diff --git a/cypress/e2e/rbac_func.cy.js b/cypress/e2e/rbac_func.cy.js index a19edf774..78697185e 100644 --- a/cypress/e2e/rbac_func.cy.js +++ b/cypress/e2e/rbac_func.cy.js @@ -90,7 +90,7 @@ describe('RBAC Functionlity Tests', () => { cy.get('[data-cy=cacheManagerStatus]').should('exist'); cy.get('[data-cy=navigationTabs]').should('exist'); cy.contains(/^\d+ Caches$/); - + cy.contains('10 Counters'); if (isMonitor) { cy.contains('1 Tasks').should('not.exist'); @@ -153,10 +153,15 @@ describe('RBAC Functionlity Tests', () => { cy.get('[data-cy=queriesTab]').should('exist'); } - //Checking cache configuration page - cy.get('[data-cy=cacheConfigurationTab]').click(); - cy.contains('authorization'); - cy.contains(roleName); + if (isSuperAdmin) { + //Checking cache configuration page + cy.get('[data-cy=cacheConfigurationTab]').click(); + cy.contains('authorization'); + cy.contains(roleName); + } else { + cy.get('[data-cy=cacheConfigurationTab]').should('not.exist'); + } + cy.get('[data-cy=manageIndexesLink]').click(); if (isSuperAdmin) { cy.get('[data-cy=clearIndexButton]').should('exist'); @@ -247,9 +252,15 @@ describe('RBAC Functionlity Tests', () => { cy.get('[data-cy=clearAllButton]').click(); cy.get('[data-cy=deleteButton]').click(); cy.wait(1500); //Waiting till the whole page is loaded - //Checking cache configuration page - cy.get('[data-cy=cacheConfigurationTab]').click(); - cy.contains('authorization').should('not.exist'); + + if (isSuperAdmin) { + //Checking cache configuration page + cy.get('[data-cy=cacheConfigurationTab]').click(); + cy.contains('authorization').should('not.exist'); + } else { + cy.get('[data-cy=cacheConfigurationTab]').should('not.exist'); + } + cy.get('[data-cy=manageIndexesLink]').click(); if (isSuperAdmin) { cy.get('[data-cy=clearIndexButton]').should('exist'); diff --git a/run-server-for-e2e-container.sh b/run-server-for-e2e-container.sh new file mode 100755 index 000000000..0bc1ff035 --- /dev/null +++ b/run-server-for-e2e-container.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e + +ABSOLUTE_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +echo ${ABSOLUTE_PATH} +docker pull quay.io/infinispan-test/server:14.0.x +docker run -d -p 11222:11222 -v ${ABSOLUTE_PATH}/scripts/identities.batch:/user-config/identities.batch -v ${ABSOLUTE_PATH}/dist:/opt/infinispan/static/console -v ${ABSOLUTE_PATH}/scripts/infinispan-basic-auth.xml:/opt/infinispan/server/conf/infinispan.xml -e JAVA_OPTIONS="-Xms1024m -Xmx3072m -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=2048m" -e IDENTITIES_BATCH="/user-config/identities.batch" quay.io/infinispan-test/server:14.0.x --node-name=infinispan-4-e2e +docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.openjdk.nashorn:nashorn-core:15.4 +docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.ow2.asm:asm:9.4 +docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.ow2.asm:asm-commons:9.4 +docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.ow2.asm:asm-tree:9.4 +docker exec $(docker ps -q -l) /opt/infinispan/bin/cli.sh install org.ow2.asm:asm-util:9.4 + +docker restart $(docker ps -q -l) + +sleep 20 +cd data; bash ./create-data.sh admin password diff --git a/run-server-for-e2e.sh b/run-server-for-e2e.sh deleted file mode 100755 index c20e466b5..000000000 --- a/run-server-for-e2e.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ $1 = "--ci" ]]; then - echo "Launch script finished" -else - trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT -fi - - -#If running on the existing server please provide the Path with this property. -EXISTING_SERVER_PATH="${EXISTING_SERVER_PATH}" -#Base directory where the server should be downloaded -BASE_DIR="server" -#The version of the server is either set as an environment variable or is the latest dev version -SERVER_VERSION="${SERVER_VERSION:-"14.0.9.Final"}" -#Root path from there the infinispan server should be downloaded -ZIP_ROOT="http://downloads.jboss.org/infinispan" -#If this environment variable is provided then it is used for downloading the server; -SERVER_DOWNLOAD_URL="${SERVER_DOWNLOAD_URL}" -#The name of the zip file; -ZIP_NAME="${ZIP_NAME:-"infinispan-server-$SERVER_VERSION.zip"}" -#The name of directory where the server should be extracted; -SERVER_UNZIP_DIR="server-unzipped" -#Path to the extracted server; -SERVER_HOME=${BASE_DIR}/${SERVER_UNZIP_DIR} - -CONF_DIR_TO_COPY_FROM="scripts/" -DATA_DIR="data/" -IS_SSL_PROCESSED=0 -#The working directory - the server is copied to this directory and later changes are done to this dir; -SERVER_DIR="infinispan-server" -#CLI command for getting the cluster size -CLUSTER_SIZE_MAIN="$SERVER_HOME/bin/cli.sh -c localhost:11322 -f batch " - -USER_NAME="admin" -PASSWORD="password" - -MONITOR_USER_NAME="monitor" -OBSERVER_USER_NAME="observer" -APPLICATION_USER_NAME="application" -DEPLOYER_USER_NAME="deployer" - -#Function prepares the server directory, i.e. downloads, extracts, copies to working directory and makes changes to configuration; -function prepareServerDir() -{ - local isCi=$1 - local confPath=$2 - local dirName=${3} - -cd ${BASE_DIR} - if [ -n "${EXISTING_SERVER_PATH}" ]; then - mkdir ${SERVER_UNZIP_DIR} - cp -r ${EXISTING_SERVER_PATH} $SERVER_UNZIP_DIR - else - if [ ! -f ${ZIP_NAME} ]; then - if [ -n "$SERVER_DOWNLOAD_URL" ]; then - wget "${SERVER_DOWNLOAD_URL}" - else - wget "$ZIP_ROOT/$SERVER_VERSION/$ZIP_NAME"; - fi - fi - - unzip -d $SERVER_UNZIP_DIR $ZIP_NAME - fi - cd .. - - if [[ -z "${SERVER_TMP}" ]]; then - SERVER_TMP=server/${SERVER_DIR} - mkdir ${SERVER_TMP} 2>/dev/null - echo "Created temporary directory: $SERVER_TMP" - - cp -r ${SERVER_HOME}/*/* $SERVER_TMP - echo "Server copied to temporary directory." - fi - - cp -r ${SERVER_HOME}/*/server ${SERVER_TMP}/${dirName} - - cp "${CONF_DIR_TO_COPY_FROM}/${confPath}" ${SERVER_TMP}/${dirName}/conf - echo "Infinispan configuration file ${confPath} copied to server ${dirName}." - - export SERVER_TMP=${SERVER_TMP} -} - -#Starts the server; -function startServer() -{ - local isCi=$1 - local confPath=$2 - local port=${3} - local nodeName=${4} - local jvmParam=${5} - - prepareServerDir "${isCi}" ${confPath} ${nodeName} - - if [[ ! -z ${port} ]]; then - portStr="-p ${port}" - fi - - $SERVER_TMP/bin/cli.sh user create ${USER_NAME} -p ${PASSWORD} -s ${nodeName} - $SERVER_TMP/bin/cli.sh user create ${MONITOR_USER_NAME} -p ${PASSWORD} -s ${nodeName} - $SERVER_TMP/bin/cli.sh user create ${OBSERVER_USER_NAME} -p ${PASSWORD} -s ${nodeName} - $SERVER_TMP/bin/cli.sh user create ${APPLICATION_USER_NAME} -p ${PASSWORD} -s ${nodeName} - $SERVER_TMP/bin/cli.sh user create ${DEPLOYER_USER_NAME} -p ${PASSWORD} -s ${nodeName} - - #Installing nashorn engine before server startup - ${SERVER_TMP}/bin/cli.sh install org.openjdk.nashorn:nashorn-core:15.4 --server-root=infinispan-4-e2e - ${SERVER_TMP}/bin/cli.sh install org.ow2.asm:asm:9.4 --server-root=infinispan-4-e2e - ${SERVER_TMP}/bin/cli.sh install org.ow2.asm:asm-commons:9.4 --server-root=infinispan-4-e2e - ${SERVER_TMP}/bin/cli.sh install org.ow2.asm:asm-tree:9.4 --server-root=infinispan-4-e2e - ${SERVER_TMP}/bin/cli.sh install org.ow2.asm:asm-util:9.4 --server-root=infinispan-4-e2e - - if [[ ${isCi} = "--ci" ]]; then - nohup $SERVER_TMP/bin/server.sh -Djavax.net.debug -Dorg.infinispan.openssl=false -c ${confPath} -s ${SERVER_TMP}/${nodeName} ${portStr:-""} --node-name=${nodeName} ${jvmParam:-} & - else - ${SERVER_TMP}/bin/server.sh -Djavax.net.debug -Dorg.infinispan.openssl=false -c ${confPath} -s ${SERVER_TMP}/${nodeName} ${portStr:-} --node-name=${nodeName} ${jvmParam:-} & - fi - - #Creating data in the server - sleep 10 - cd $DATA_DIR/ - ./create-data.sh ${USER_NAME} ${PASSWORD} -} - -#deleting the testable server directory -rm -drf server/${SERVER_DIR} -rm -drf server/${SERVER_UNZIP_DIR} - -export JAVA_OPTS="-Xms512m -Xmx1024m -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=512m" - -startServer "$1" infinispan-basic-auth.xml 11222 infinispan-4-e2e -echo "Infinispan Server for E2E tests has started." - - -if [[ $1 = "--ci" ]]; then - echo "Launch script finished" -else - # Wait until script stopped - while : - do - sleep 5 - done -fi diff --git a/src/__tests__/services/cacheConfigUtils.test.ts b/src/__tests__/services/cacheConfigUtils.test.ts index d53c90b17..1e486d5c8 100644 --- a/src/__tests__/services/cacheConfigUtils.test.ts +++ b/src/__tests__/services/cacheConfigUtils.test.ts @@ -1,21 +1,43 @@ -import { CacheConfigUtils, Distributed, Invalidated, Local, Replicated, Scattered } from '@services/cacheConfigUtils'; +import { + CacheConfigUtils, + DIST_ASYNC, + DIST_SYNC, + Distributed, + Invalidated, + INVALIDATION_ASYNC, + INVALIDATION_SYNC, + LOCAL, + Local, + REPL_ASYNC, + REPL_SYNC, + Replicated +} from '@services/cacheConfigUtils'; import { ContentType, EncodingType } from '@services/infinispanRefData'; describe('Cache Config Utils tests', () => { test('cache topology', () => { - let distributed = '{\n' + ' "distributed-cache": {\n' + ' }\n' + '}'; - - expect(CacheConfigUtils.mapCacheType(JSON.parse(distributed))).toBe('Distributed'); - - let replicated = '{\n' + ' "replicated-cache": {\n' + ' }\n' + '}'; - - expect(CacheConfigUtils.mapCacheType(JSON.parse(replicated))).toBe('Replicated'); + expect(CacheConfigUtils.mapCacheType(DIST_SYNC)).toBe('Distributed'); + expect(CacheConfigUtils.mapCacheType(DIST_ASYNC)).toBe('Distributed'); + expect(CacheConfigUtils.mapCacheType(REPL_SYNC)).toBe('Replicated'); + expect(CacheConfigUtils.mapCacheType(REPL_ASYNC)).toBe('Replicated'); + expect(CacheConfigUtils.mapCacheType(LOCAL)).toBe('Local'); + expect(CacheConfigUtils.mapCacheType(INVALIDATION_ASYNC)).toBe('Invalidated'); + expect(CacheConfigUtils.mapCacheType(INVALIDATION_SYNC)).toBe('Invalidated'); expect(CacheConfigUtils.mapCacheType(Distributed)).toBe('Distributed'); expect(CacheConfigUtils.mapCacheType(Replicated)).toBe('Replicated'); - expect(CacheConfigUtils.mapCacheType(Local)).toBe('Local'); expect(CacheConfigUtils.mapCacheType(Invalidated)).toBe('Invalidated'); - expect(CacheConfigUtils.mapCacheType(Scattered)).toBe('Scattered'); + expect(CacheConfigUtils.mapCacheType(Local)).toBe('Local'); + }); + + test('cache sync or async', () => { + expect(CacheConfigUtils.isAsync(DIST_SYNC)).toBe(false); + expect(CacheConfigUtils.isAsync(DIST_ASYNC)).toBe(true); + expect(CacheConfigUtils.isAsync(REPL_SYNC)).toBe(false); + expect(CacheConfigUtils.isAsync(REPL_ASYNC)).toBe(true); + expect(CacheConfigUtils.isAsync(LOCAL)).toBe(false); + expect(CacheConfigUtils.isAsync(INVALIDATION_ASYNC)).toBe(true); + expect(CacheConfigUtils.isAsync(INVALIDATION_SYNC)).toBe(false); }); test('editable', () => { diff --git a/src/app/CacheManagers/CacheTableDisplay.tsx b/src/app/CacheManagers/CacheTableDisplay.tsx index 10f8ebd25..9b3c17702 100644 --- a/src/app/CacheManagers/CacheTableDisplay.tsx +++ b/src/app/CacheManagers/CacheTableDisplay.tsx @@ -105,9 +105,9 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb setChipsCacheType([]); } else { // From the select, extract each category - let filterStatus = extract(selectedFilters, cacheStatus); - let filterFeatures = extract(selectedFilters, cacheFeatures); - let filterCacheType = extract(selectedFilters, cacheTypes); + const filterStatus = extract(selectedFilters, cacheStatus); + const filterFeatures = extract(selectedFilters, cacheFeatures); + const filterCacheType = extract(selectedFilters, cacheTypes); // Update chips setChipsCacheStatus(filterStatus); setChipsCacheFeature(filterFeatures); @@ -185,7 +185,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb const actionResolver = (rowData: IRowData, extraData: IExtraData) => { // @ts-ignore - let cacheName: string = rowData.cells[0].cacheName as string; + const cacheName: string = rowData.cells[0].cacheName as string; if (!cacheName) { return []; @@ -213,7 +213,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb ]; } - let actions = [ + const actions = [ { 'data-cy': 'deleteCacheAction', title: t('cache-managers.delete'), @@ -256,14 +256,10 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb }; const closeIgnoreModal = (ignoreDone: boolean) => { - if (ignoreDone) { - } setCacheAction({ cacheName: '', action: '' }); }; const closeAvailableModal = (ignoreDone: boolean) => { - if (ignoreDone) { - } setCacheAction({ cacheName: '', action: '' }); }; @@ -485,7 +481,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb }; const onDeleteChip = (chip) => { - let actualSelection = selectedFilters.filter((item) => item !== chip); + const actualSelection = selectedFilters.filter((item) => item !== chip); setSelectedFilters(actualSelection); }; @@ -569,7 +565,6 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb - diff --git a/src/app/Caches/CacheMetrics.tsx b/src/app/Caches/CacheMetrics.tsx index aacdb1b7f..46a416ab9 100644 --- a/src/app/Caches/CacheMetrics.tsx +++ b/src/app/Caches/CacheMetrics.tsx @@ -28,36 +28,37 @@ import { useTranslation } from 'react-i18next'; import { StorageType } from '@services/infinispanRefData'; import { DataAccessChart } from './DataAccessChart'; import { useCacheDetail } from '@app/services/cachesHook'; +import { ConsoleACL } from '@services/securityService'; +import { ConsoleServices } from '@services/ConsoleServices'; +import { useConnectedUser } from '@app/services/userManagementHook'; const CacheMetrics = (props: { cacheName: string; display: boolean }) => { + const { connectedUser } = useConnectedUser(); const { cache, error, loading } = useCacheDetail(); const [stats, setStats] = useState(cache.stats); const [displayQueryStats, setDisplayQueryStats] = useState(false); const [memory, setMemory] = useState(undefined); const { t } = useTranslation(); const brandname = t('brandname.brandname'); + const [displayDataDistribution, setDisplayDataDistribution] = useState(false); useEffect(() => { - const loadMemory = JSON.parse(cache.configuration.config)[props.cacheName]; - const cacheMode = Object.keys(loadMemory)[0]; - if (loadMemory[cacheMode].memory) { - if (loadMemory[cacheMode].memory.storage === 'HEAP' && loadMemory[cacheMode].memory['max-size']) - setMemory(loadMemory[cacheMode].memory.storage); - else if (loadMemory[cacheMode].memory.storage === 'OFF_HEAP') setMemory(loadMemory[cacheMode].memory.storage); - } else { - setMemory('HEAP'); + if (ConsoleServices.security().hasConsoleACL(ConsoleACL.ADMIN, connectedUser)) { + // Data distribution is for admin only + setDisplayDataDistribution(true); + const loadMemory = cache.memory; + if (loadMemory) { + setMemory(loadMemory.storage_type == 'OFF_HEAP' ? StorageType.OFF_HEAP : StorageType.HEAP); + } else { + setMemory(StorageType.HEAP); + } } setStats(cache.stats); - let loadQueryStats = cache.stats != undefined && cache.stats.enabled && cache.features.indexed; + const loadQueryStats = cache.stats != undefined && cache.stats.enabled && cache.features.indexed; setDisplayQueryStats(loadQueryStats); }, [cache, error]); - useEffect(() => { - if (props.display) { - } - }, []); - const buildOperationsPerformanceCard = () => { if (!stats) { return ''; @@ -192,7 +193,7 @@ const CacheMetrics = (props: { cacheName: string; display: boolean }) => { content = ( - {displayUtils.formatNumber(stats.data_memory_used)} + {displayUtils.formatNumber(stats.data_memory_used)} { {buildEntriesCard()} {buildMemoryCard()} {buildOperationsPerformanceCard()} - - - + {displayDataDistribution && + + + + } - {buildQueryStats()} + {buildQueryStats()} ); }; diff --git a/src/app/Caches/DataDistributionChart.tsx b/src/app/Caches/DataDistributionChart.tsx index ce719c5ec..b241640f8 100644 --- a/src/app/Caches/DataDistributionChart.tsx +++ b/src/app/Caches/DataDistributionChart.tsx @@ -42,10 +42,7 @@ const DataDistributionChart = (props: { cacheName: string }) => { const [displayMemoryUsed, setDisplayMemoryUsed] = useState(false); useEffect(() => { - // To display memory_used - const loadMemory = JSON.parse(cache.configuration.config)[props.cacheName]; - const cacheMode = Object.keys(loadMemory)[0]; - loadMemory[cacheMode].memory && loadMemory[cacheMode].memory['max-size'] + cache.memory && cache.memory.max_size.length > 0 ? setDisplayMemoryUsed(true) : setDisplayMemoryUsed(false); }, [cache, dataDistribution]); diff --git a/src/app/Caches/DetailCache.tsx b/src/app/Caches/DetailCache.tsx index b7ef1622c..55969e01e 100644 --- a/src/app/Caches/DetailCache.tsx +++ b/src/app/Caches/DetailCache.tsx @@ -72,16 +72,18 @@ const DetailCache = (props: { cacheName: string }) => { if (!ConsoleServices.security().hasCacheConsoleACL(ConsoleACL.READ, cacheName, connectedUser)) { return ''; } - const encoding = CacheConfigUtils.toEncoding(cache.configuration.config); + const encodingKey = CacheConfigUtils.toEncoding(cache.encoding.key); + const encodingValue = CacheConfigUtils.toEncoding(cache.encoding.value); - if (encoding == EncodingType.Java || encoding == EncodingType.JBoss) { + if (encodingKey == EncodingType.Java || encodingKey == EncodingType.JBoss || + encodingValue == EncodingType.Java || encodingValue == EncodingType.JBoss) { return ( window.open(encodingDocs, '_blank')}> @@ -145,29 +147,6 @@ const DetailCache = (props: { cacheName: string }) => { ); }; - const encodingMessage = () => { - if (loading || !cache || error) { - return ''; - } - - const encodingType = CacheConfigUtils.toEncoding(cache.configuration.config); - if (encodingType == EncodingType.Protobuf) { - return ''; - } - return ( - window.open(encodingDocs, '_blank')}> - {t('caches.configuration.encoding-docs-message')} - - } - /> - ); - }; - const buildDetailContent = () => { if (error.length > 0) { return ( @@ -205,19 +184,34 @@ const DetailCache = (props: { cacheName: string }) => { ); } + const displayEntries: boolean = cache.editable && + ConsoleServices.security().hasCacheConsoleACL(ConsoleACL.READ, cacheName, connectedUser); + + const displayConf: boolean = ConsoleServices.security().hasConsoleACL(ConsoleACL.ADMIN, connectedUser); + if ( - activeTabKey1 == 0 && - cache.editable && - ConsoleServices.security().hasCacheConsoleACL(ConsoleACL.READ, cacheName, connectedUser) - ) { + activeTabKey1 == 0 && displayEntries) { return {buildEntriesTabContent(cache.queryable)}; - } else if (activeTabKey1 == 2) { - return ; - } else { + } + + if (displayConf && (activeTabKey1 == 1 || (!displayEntries && activeTabKey1 == 0))) { + return ( + cache.configuration && + ); + } + let metricsKey = 2; + if (!displayConf) { + metricsKey --; + } + if (!displayEntries) { + metricsKey--; + } + if (activeTabKey1 == metricsKey) { return ( - + ); } + return ''; }; const buildBackupsManage = () => { @@ -406,9 +400,13 @@ const DetailCache = (props: { cacheName: string }) => { }; const displayConfiguration = () => { + if (!ConsoleServices.security().hasConsoleACL(ConsoleACL.ADMIN, connectedUser)) { + return ; + } + let eventKey = 1; if (!ConsoleServices.security().hasCacheConsoleACL(ConsoleACL.READ, cacheName, connectedUser) || !cache.editable) { - eventKey = 0; + eventKey--; } return ; @@ -419,10 +417,19 @@ const DetailCache = (props: { cacheName: string }) => { return ''; } + let eventKey = 2; + if (!ConsoleServices.security().hasCacheConsoleACL(ConsoleACL.READ, cacheName, connectedUser) || !cache.editable) { + eventKey--; + } + + if (!ConsoleServices.security().hasConsoleACL(ConsoleACL.ADMIN, connectedUser)) { + eventKey--; + } + return ( ); @@ -471,9 +478,11 @@ const DetailCache = (props: { cacheName: string }) => { {cache.name} - - - + {cache.type !== 'Unknown' && + + + + } {buildShowMoreHeader()} @@ -486,9 +495,6 @@ const DetailCache = (props: { cacheName: string }) => { component={TabsComponent.nav} onSelect={(event, tabIndex) => { setActiveTabKey1(tabIndex); - if (tabIndex == 0 || tabIndex == 2) { - loadCache(cacheName); - } }} > {displayCacheEntries()} diff --git a/src/app/Caches/Query/QueryEntries.tsx b/src/app/Caches/Query/QueryEntries.tsx index df8f9d812..8bcaded6e 100644 --- a/src/app/Caches/Query/QueryEntries.tsx +++ b/src/app/Caches/Query/QueryEntries.tsx @@ -153,8 +153,8 @@ const QueryEntries: React.FunctionComponent = (props: { return ( - ); diff --git a/src/app/assets/languages/en.json b/src/app/assets/languages/en.json index 0288a2b40..36fb30c0c 100644 --- a/src/app/assets/languages/en.json +++ b/src/app/assets/languages/en.json @@ -418,7 +418,7 @@ "table-label": "Create caches from custom templates.", "no-encoding-warning": "This cache either has no encoding configuration or uses an encoding that the console does not support. You must use Hot Rod clients to perform read and write operations on this cache.", "encoding-docs-message": "See \"Cache Encoding and Marshalling\" in the documentation.", - "pojo-encoding": "This cache uses \"{{encoding}}\" encoding. {{brandname}} Console might not fully support data manipulation or displaying entries for caches that don't use ProtoStream encoding." + "pojo-encoding": "This cache uses \"{{encodingKey}}\" for keys and \"{{encodingValue}}\" encoding for values. {{brandname}} Console might not fully support data manipulation or displaying entries for caches that don't use ProtoStream encoding." }, "tabs": { "entries": "Entries", @@ -512,6 +512,7 @@ "add-entry-form-value-type-select-label": "Select Value Content Type" }, "query": { + "query-metrics-button": "View all query metrics", "query-metrics-title": "Query metrics", "query-metrics-tooltip": "Average time, in nanoseconds, for queries on this cache.", "button-clear-query-stats": "Clear all stats", @@ -563,9 +564,9 @@ "data-access-title": "Data access", "data-access-expanded": "Show more information", "data-access-collapsed": "Show less information", - "average-reads": "Average cache read time", - "average-writes": "Average cache write time", - "average-deletes": "Average cache delete time", + "average-reads": "Avg. cache read time", + "average-writes": "Avg. cache write time", + "average-deletes": "Avg. cache delete time", "average-reads-tooltip": "Average time, in milliseconds, for read operations to complete.", "average-writes-tooltip": "Average time, in milliseconds, for write operations to complete.", "average-deletes-tooltip": "Average time, in milliseconds, for delete operations to complete.", @@ -583,17 +584,17 @@ "data-access-remove-hits-info": "Remove hits displays the number of successful requests to delete cache entries.", "data-access-remove-misses-info": "Remove misses displays the number of times that a client attempted to delete an entry that was not in the cache.", "data-access-evictions-info": "Evictions displays the number of entries that have been evicted from the cache. Eviction removes entries from the cache when it reaches a configured threshold.", - "approximate-entries": "Approximate number of entries", + "approximate-entries": "Approx. number of entries", "approximate-entries-tooltip": "Displays the approximate number of entries that are stored in the cache. May include replicas.", - "approximate-unique-entries": "Approximate unique number of entries", + "approximate-unique-entries": "Approx. unique number of entries", "approximate-unique-entries-tooltip": "Displays the approximate unique number of entries that are stored in the cache. Each entry is counted only once so the number does not include replicas.", - "approximate-entries-in-memory": "Approximate entries in memory", + "approximate-entries-in-memory": "Approx. entries in memory", "approximate-entries-in-memory-tooltip": "Displays the approximate number of entries in memory. May include replicas.", "min-nodes": "Minimum number of nodes", "min-nodes-tooltip": "Displays the number of nodes that must remain in the cluster to avoid data loss.", - "cache-size-off-heap": "Size of cache in off-heap memory", + "cache-size-off-heap": "Size in off-heap memory", "cache-size-off-heap-tooltip": "Displays the amount of off-heap memory that data in the cache uses. Total memory usage is higher because it includes internal data structures.", - "cache-size-heap": "Size of cache in heap memory", + "cache-size-heap": "Size in heap memory", "cache-size-heap-tooltip": "Displays the amount of JVM heap memory that data in the cache uses. Total memory usage is higher because it includes internal data structures.", "data-distribution": "Data distribution", "data-distribution-title": "Data for nodes", diff --git a/src/services/cacheConfigUtils.ts b/src/services/cacheConfigUtils.ts index 2cfc108fe..a3d188108 100644 --- a/src/services/cacheConfigUtils.ts +++ b/src/services/cacheConfigUtils.ts @@ -10,6 +10,13 @@ export const Replicated = 'replicated-cache'; export const Invalidated = 'invalidation-cache'; export const Local = 'local-cache'; export const Scattered = 'scattered-cache'; +export const LOCAL = 'LOCAL'; +export const REPL_SYNC = 'REPL_SYNC'; +export const REPL_ASYNC = 'REPL_ASYNC'; +export const INVALIDATION_SYNC = 'INVALIDATION_SYNC'; +export const INVALIDATION_ASYNC = 'INVALIDATION_ASYNC'; +export const DIST_SYNC = 'DIST_SYNC'; +export const DIST_ASYNC = 'DIST_ASYNC'; /** * Utility class to map cache configuration @@ -66,26 +73,58 @@ export class CacheConfigUtils { return EncodingType.Empty; } + /** + * Map cache name from cache mode + * @param mode or cache type name + */ + public static mapCacheTypeFromCacheMode(mode: string): string { + if (mode.includes('DIST')) { + return 'Distributed'; + } + + if (mode.includes('REPL')) { + return 'Replicated'; + } + + if (mode.includes('LOCAL')) { + return 'Local'; + } + + if (mode.includes('INVALIDATION')) { + return 'Invalidated'; + } + + return 'Unknown'; + } + /** * Map cache name from json configuration or from the label in the conf * @param config or cache type name */ - public static mapCacheType(config: JSON | string): string { - let cacheType: string = 'Unknown'; - if (config.hasOwnProperty(Distributed) || config == Distributed) { + public static mapCacheType(type: string | undefined): string { + let cacheType = 'Unknown'; + if (!type) { + return cacheType; + } + + if (type.includes('DIST') || type == Distributed) { cacheType = 'Distributed'; - } else if (config.hasOwnProperty(Replicated) || config == Replicated) { + } else if (type.includes('REPL') || type == Replicated) { cacheType = 'Replicated'; - } else if (config.hasOwnProperty(Local) || config == Local) { + } else if (type.includes('LOCAL') || type == Local) { cacheType = 'Local'; - } else if (config.hasOwnProperty(Invalidated) || config == Invalidated) { + } else if (type.includes('INVALIDATION') || type == Invalidated) { cacheType = 'Invalidated'; - } else if (config.hasOwnProperty(Scattered) || config == Scattered) { + } else if (type.includes('SCAT')|| type == Scattered) { cacheType = 'Scattered'; } return cacheType; } + public static isAsync(type: string): boolean { + return type.includes('ASYNC'); + } + /** * Retrieve if an encoding is editable or not * @@ -101,7 +140,7 @@ export class CacheConfigUtils { * @param encodingType */ public static getContentTypeOptions(encodingType: EncodingType): ContentType[] { - let contentTypes: ContentType[] = []; + const contentTypes: ContentType[] = []; if ( encodingType == EncodingType.Java || diff --git a/src/services/cacheService.ts b/src/services/cacheService.ts index be3f269f0..cc205990c 100644 --- a/src/services/cacheService.ts +++ b/src/services/cacheService.ts @@ -30,22 +30,38 @@ export class CacheService { cacheStats = data.stats; cacheStats.enabled = data.statistics; } - let keyValueEncoding = { + const keyValueEncoding = { key: CacheConfigUtils.toEncoding(data['key_storage']), value: CacheConfigUtils.toEncoding(data['value_storage']) }; + const config = data['configuration'] ? { + name: cacheName, + config: JSON.stringify(data.configuration[cacheName], null, 2) + } : undefined; + + let cacheMemory: undefined | CacheMemory = undefined; + if (data['storage_type']) { + cacheMemory = { + storage_type: data['storage_type'], + max_size: data['max_size'], + max_size_bytes: data['max_size_bytes'] + }; + } + + const cacheType = CacheConfigUtils.mapCacheType(data['mode']); + const async = CacheConfigUtils.isAsync(data['mode']); return { name: cacheName, started: true, - type: CacheConfigUtils.mapCacheType(data.configuration[cacheName]), + type: cacheType, encoding: keyValueEncoding, size: data['size'], rehash_in_progress: data['rehash_in_progress'], indexing_in_progress: data['indexing_in_progress'], rebalancing_enabled: data['rebalancing_enabled'], - editable: + editable: data.indexed || ( CacheConfigUtils.isEditable(keyValueEncoding.key as EncodingType) && - CacheConfigUtils.isEditable(keyValueEncoding.value as EncodingType), + CacheConfigUtils.isEditable(keyValueEncoding.value as EncodingType)), queryable: data.queryable, features: { bounded: data.bounded, @@ -55,11 +71,10 @@ export class CacheService { secured: data.secured, hasRemoteBackup: data['has_remote_backup'] }, - configuration: { - name: cacheName, - config: JSON.stringify(data.configuration, null, 2) - }, - stats: cacheStats + configuration: config, + stats: cacheStats, + memory: cacheMemory, + async: async, }; }); } diff --git a/src/types/InfinispanTypes.ts b/src/types/InfinispanTypes.ts index 13a274d34..733d0c93a 100644 --- a/src/types/InfinispanTypes.ts +++ b/src/types/InfinispanTypes.ts @@ -110,7 +110,7 @@ interface CacheEncoding { interface DetailedInfinispanCache { name: string; - configuration: CacheConfig; + configuration?: CacheConfig; encoding: CacheEncoding; type: string; started: boolean; @@ -121,8 +121,10 @@ interface DetailedInfinispanCache { editable: boolean; queryable: boolean; features: Features; + async: boolean; backupSites?: [XSite]; stats?: CacheStats; + memory?: CacheMemory; } interface CacheStats { @@ -153,6 +155,12 @@ interface CacheStats { required_minimum_number_of_nodes: number; } +interface CacheMemory { + storage_type: string; + max_size: string; + max_size_bytes: number; +} + interface StateTransferStatus { site: string; status: Status;