From c6514c51bc716d5d7aa7cfbe425bf15385ce4d42 Mon Sep 17 00:00:00 2001 From: Elson9 Date: Thu, 6 Jun 2024 09:06:15 -0700 Subject: [PATCH] Merge in display name backend changes (#1064) * changes for local dev w/ npm * Cypress astra local (#1022) * Added Astra scans to e2e tests --------- Co-authored-by: ike thecoder Co-authored-by: Joshua Jones Co-authored-by: Elson9 Co-authored-by: Russell Vinegar <38586679+rustyjux@users.noreply.github.com> Co-authored-by: Russell Vinegar * changes for local dev w/ npm * Move ns dropdown * save and load recently viewed gws * search namespaces * oops - remove local dev changes * fix UserData.userId property reference * fix ip * maintain focus, style, fix type errors * ns to gw in text * include current gw in recently viewed * add query to whitelist * remove current gw from recently viewed * edit display name - WIP, query and mutation inc * touch up ns search * style edit display name * add uma2 test and service * add displayName to mocks * show ns displayname on namespaces page * add new graphql for updating display name * add the enforcement on the list extension * integrate into frontend * upd unit tests * add graphql whitelist query * fix for no namespace on name * return displayName from currentNamespace query --------- Co-authored-by: Russell Vinegar Co-authored-by: nirajCITZ <94716060+nirajCITZ@users.noreply.github.com> Co-authored-by: ike thecoder Co-authored-by: Joshua Jones Co-authored-by: Russell Vinegar <38586679+rustyjux@users.noreply.github.com> --- .env.local | 9 +- .github/astra-jira.py | 127 ++++++++++++ .github/workflows/aps-cypress-e2e.yaml | 53 ++--- .github/workflows/ci-feat-sonar.yaml | 2 +- .gitignore | 6 +- docker-compose.yml | 28 ++- e2e/cypress.config.ts | 3 +- e2e/cypress/fixtures/state/scanID.json | 4 + e2e/cypress/fixtures/state/scanResult.json | 0 e2e/cypress/support/auth-commands.ts | 44 +++- e2e/cypress/support/global.d.ts | 6 + e2e/cypress/support/util-commands.ts | 45 +++++ .../tests/01-api-key/01-create-api.cy.ts | 4 +- e2e/cypress/tests/01-api-key/09-gwa-get.ts | 4 +- ...client-cred-create-api-prod-auth-pro.cy.ts | 4 +- .../09-update-product-env/06-shared-idp.cy.ts | 10 +- .../10-clear-resources/01-create-api.cy.ts | 4 +- .../11-activity-feed/01-activity-feed.cy.ts | 12 +- .../02-activity-feed-failure.cy.ts | 6 +- .../12-access-permission/01-create-api.cy.ts | 4 +- .../09-content-publish.cy.ts | 6 +- .../tests/15-aps-api/01-create-api.cy.ts | 4 +- .../tests/15-aps-api/02-organization.cy.ts | 75 ++++--- .../tests/15-aps-api/03-documentation.cy.ts | 65 +++--- .../15-aps-api/05-authorizationProfiles.cy.ts | 57 +++--- .../tests/15-aps-api/06-products.cy.ts | 43 ++-- .../tests/15-aps-api/07-api-directory.cy.ts | 94 +++++---- .../tests/15-aps-api/08-namespaces.cy.ts | 58 +++--- .../01-store-and-scan-astra-result.ts | 5 + local/astra/Dockerfile | 27 +++ ...plocalhost4180managernamespaces-339568.gql | 2 + ...plocalhost4180managernamespaces-b2df18.gql | 1 + ...ocaltestme4180managernamespaces-a5ae8c.gql | 4 + src/authz/matrix.csv | 1 + src/lists/extensions/Namespace.ts | 35 ++++ src/mocks/handlers.js | 2 + src/mocks/resolvers/namespace-access.js | 2 + .../components/auth-action/auth-action.tsx | 6 - .../edit-display-name/edit-display-name.tsx | 172 ++++++++++++++++ .../components/edit-display-name/index.ts | 1 + .../namespace-menu/namespace-menu.tsx | 191 +++++++++++++----- src/nextapp/components/nav-bar/nav-bar.tsx | 125 +++++++----- src/nextapp/pages/_app.tsx | 5 +- .../pages/manager/namespaces/index.tsx | 145 +++++++------ .../public/images/no_results_folder.png | Bin 0 -> 10322 bytes .../shared/hooks/use-current-namespace.ts | 2 + src/nextapp/shared/types/app.types.ts | 1 + src/nextapp/types.ts | 1 + src/services/keycloak/namespace-details.ts | 1 + src/services/keycloak/token-service.ts | 23 ++- .../uma2/resource-registration-service.ts | 57 +++++- .../uma2/integrated-permticket.test.ts | 116 +++++++++++ .../uma2/integrated-resources.test.ts | 71 +++++++ 53 files changed, 1357 insertions(+), 416 deletions(-) create mode 100644 .github/astra-jira.py create mode 100644 e2e/cypress/fixtures/state/scanID.json create mode 100644 e2e/cypress/fixtures/state/scanResult.json create mode 100644 e2e/cypress/tests/18-scan-astra-result/01-store-and-scan-astra-result.ts create mode 100644 local/astra/Dockerfile create mode 100644 src/authz/graphql-whitelist/httpoauth2proxylocaltestme4180managernamespaces-a5ae8c.gql create mode 100644 src/nextapp/components/edit-display-name/edit-display-name.tsx create mode 100644 src/nextapp/components/edit-display-name/index.ts create mode 100644 src/nextapp/public/images/no_results_folder.png create mode 100644 src/test/services/uma2/integrated-permticket.test.ts create mode 100644 src/test/services/uma2/integrated-resources.test.ts diff --git a/.env.local b/.env.local index 11f701031..2582321bd 100644 --- a/.env.local +++ b/.env.local @@ -1,4 +1,4 @@ -LOG_LEVEL=info +LOG_LEVEL=debug DISABLE_LOGGING='true' AUTH_STRATEGY=Oauth2Proxy KNEX_HOST=kong-db @@ -31,3 +31,10 @@ NEXT_PUBLIC_DEVELOPER_IDS=idir,bceid,bcsc,github NEXT_PUBLIC_PROVIDER_IDS=idir NEXT_PUBLIC_ACCOUNT_BCEID_URL=https://www.test.bceid.ca/logon.aspx?returnUrl=/profile_management NEXT_PUBLIC_ACCOUNT_BCSC_URL=https://idtest.gov.bc.ca/account/ + +# For automated integrated testing +TEST_PORTAL_CLIENT_ID=aps-portal +TEST_PORTAL_CLIENT_SECRET=8e1a17ed-cb93-4806-ac32-e303d1c86018 +TEST_PORTAL_USERNAME=janis@idir +TEST_PORTAL_PASSWORD=awsummer + diff --git a/.github/astra-jira.py b/.github/astra-jira.py new file mode 100644 index 000000000..8f6dd9520 --- /dev/null +++ b/.github/astra-jira.py @@ -0,0 +1,127 @@ +import os +from datetime import datetime +import json +from pathlib import Path +import requests +from requests.auth import HTTPBasicAuth +import sys + +ASTRA_SCAN_RESULTS = os.environ.get('ASTRA_SCAN_RESULTS') + +JIRA_EMAIL = os.environ.get('JIRA_EMAIL') +JIRA_API_KEY = os.environ.get('JIRA_API_KEY') +JIRA_API_URL = "https://dpdd.atlassian.net/rest/api/2" +JIRA_AUTH = HTTPBasicAuth(JIRA_EMAIL, JIRA_API_KEY) +HEADERS = { + "Accept": "application/json", + "Content-Type": "application/json" +} + +IMPACT_LEVELS = ["Medium", "High"] + +date = datetime.now() +date_str = date.strftime("%d %b %Y") +scan_name = f'{date_str} - Astra Scan Results' + +def check_results(scan_result): + """ + Check if there are any significant vulnerabilities + """ + vulnerabilities = [vulnerability for sublist in scan_result for vulnerability in sublist] + for vulnerability in vulnerabilities: + if vulnerability["impact"] in IMPACT_LEVELS: + print('Issues found!') + return True + + return False + +def format_ticket(scan_results): + """ + Converts vulnerabilities into format that can be posted to Jira. + """ + description = 'See attached scan results for more details' + for sublist in scan_results: + for vulnerability in sublist: + if vulnerability["impact"] in IMPACT_LEVELS: + description += f'\n\n*Name: {vulnerability["name"]}*\n' + description += f'Impact: {vulnerability["impact"]}\n' + description += f'Description: {vulnerability["Description"]}\n' + description += f'Remediation: {vulnerability["remediation"]}\n' + description += f'URL: {vulnerability["url"]}\n' + + return {'summary': scan_name, 'description': description} + +def filter_vulnerabilities(scan_results): + """ + Filter vulnerabilities with medium and high severity to attach. + """ + filtered_vulnerabilities = [] + + for sublist in scan_results: + for vulnerability in sublist: + if vulnerability["impact"] in IMPACT_LEVELS: + filtered_vulnerabilities.append(vulnerability) + + filtered_vulnerabilities_json = json.dumps(filtered_vulnerabilities, indent=4) + filtered_vulnerabilities_bytes = filtered_vulnerabilities_json.encode() + + return filtered_vulnerabilities_bytes + +def post_request(ticket, scan_results_data): + """ + Post issue request to Jira. + """ + payload = json.dumps({ + "fields": { + "project": { + "key": "APS" + }, + "summary": ticket['summary'], + "description": ticket['description'], + "issuetype": { + "name": "Story" + }, + "customfield_10014": "APS-908", + "priority": { + "id": "10000" + } + } + }) + + post_url = JIRA_API_URL + '/issue' + + response = requests.post(url=post_url, data=payload, + headers=HEADERS, auth=JIRA_AUTH) + + print(response.text) + + if response.status_code != 201: + print("Error occurred while creating Jira issue:", response.text) + return + + # Attach scan results to the Jira issue + issue_key = response.json().get('key') + attach_url = f"{JIRA_API_URL}/issue/{issue_key}/attachments" + headers = {"X-Atlassian-Token": "nocheck"} + filename = scan_name + '.json' + attach_response = requests.post(url=attach_url, files={'file': (filename, scan_results_data)}, headers=headers, auth=JIRA_AUTH) + + if attach_response.status_code == 200: + print("Jira issue created and file attached successfully!") + else: + print("Error occurred while attaching file to Jira issue:", attach_response.text) + +def main(): + with open(ASTRA_SCAN_RESULTS, "r") as file: + scan_results = json.load(file) + vulnerabilities = check_results(scan_results) + + if vulnerabilities: + ticket_data = {} + ticket_data = format_ticket(scan_results) + filtered_vulnerabilities = filter_vulnerabilities(scan_results) + post_request(ticket_data, filtered_vulnerabilities) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/.github/workflows/aps-cypress-e2e.yaml b/.github/workflows/aps-cypress-e2e.yaml index 38de9e387..796d74af9 100644 --- a/.github/workflows/aps-cypress-e2e.yaml +++ b/.github/workflows/aps-cypress-e2e.yaml @@ -28,31 +28,6 @@ jobs: - name: Checkout Portal uses: actions/checkout@v2 - # - name: Determine Download file name - # id: set_variable - # run: | - # echo ${{ runner.arch }} - # if [ "${{ runner.arch }}" == "X64" ]; then - # echo "::set-output name=my_variable::gwa_Linux_x86_64.tgz" - # elif [ "${{ runner.arch }}" == "ARM64" ]; then - # echo "::set-output name=my_variable::gwa_Linux_arm64.tgz" - # else - # echo "unsupported architecture" - # fi - - # - name: Download Binary - # uses: robinraju/release-downloader@v1.8 - # with: - # repository: "bcgov/gwa-cli" - # latest: true - # fileName: ${{ steps.set_variable.outputs.my_variable }} - # out-file-path: "${{ github.workspace }}/e2e" - - # - name: Unzip file - # run: | - # cd ${{ github.workspace }}/e2e - # tar xvzf ${{ steps.set_variable.outputs.my_variable }} - - name: Build Docker Images run: | docker compose --profile testsuite build @@ -89,6 +64,12 @@ jobs: name: test-results path: ${{ github.workspace }}/e2e/results/report + - name: Upload E2E Code Coverage Report + uses: actions/upload-artifact@v2 + with: + name: code-coverage + path: ${{ github.workspace }}/e2e/coverage + - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master with: @@ -113,4 +94,26 @@ jobs: echo -e "Stats: $STATS\n\nFailed Tests:\n$FAILED_TESTS\n\nRun Link: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" > msg export MSG=$(cat msg) gh issue create --title "FAILED: Automated Tests($FAILURE_COUNT)" --body "$MSG" --label "automation" --assignee "${{ env.GIT_COMMIT_AUTHOR }}" + exit 1 fi + + - name: Set up Python 3.9 + if: failure() + uses: actions/setup-python@v2 + with: + python-version: "3.9" + architecture: "x64" + + - name: Install Python dependencies + if: failure() + run: | + python -m pip install --upgrade pip + pip install requests + + - name: Check Astra results and create Jira issue if necessary + if: failure() + run: python .github/astra-jira.py + env: + JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }} + JIRA_API_KEY: ${{ secrets.JIRA_API_KEY }} + ASTRA_SCAN_RESULTS: ${{ github.workspace }}/e2e/cypress/fixtures/state/scanResult.json diff --git a/.github/workflows/ci-feat-sonar.yaml b/.github/workflows/ci-feat-sonar.yaml index 34ccb59ea..efcf4a03a 100644 --- a/.github/workflows/ci-feat-sonar.yaml +++ b/.github/workflows/ci-feat-sonar.yaml @@ -23,7 +23,7 @@ jobs: - name: Run Tests run: | - docker compose up kong-db -d + docker compose up keycloak -d set -o allexport source ./.env.local diff --git a/.gitignore b/.gitignore index 53680afd1..36030af40 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,10 @@ kc.js # vs code settings .vscode + e2e/cypress/fixtures/*-plugin.yml e2e/cypress/fixtures/state/*.pub -e2e/cypress/fixtures/state/*.pem \ No newline at end of file +e2e/cypress/fixtures/state/*.pem +e2e/cypress/fixtures/state/scanResult.json +e2e/cypress/fixtures/state/scanID.json +e2e/cypress/downloads/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d75a52715..b6e49d684 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -237,5 +237,31 @@ services: - aps-net profiles: - testsuite + astra-mongo: + image: mongo:4.2.2 + container_name: astra-mongo + ports: + - '27017:27017' + networks: + aps-net: + aliases: + - mongo.localtest.me + astra-gui: + build: + context: local/astra + container_name: astra-gui + restart: always + environment: + MONGO_PORT_27017_TCP_ADDR: astra-mongo + networks: + aps-net: + aliases: + - astra.localtest.me + depends_on: + - astra-mongo + links: + - astra-mongo:mongo + ports: + - '8094:8094' networks: - aps-net: {} + aps-net: {} \ No newline at end of file diff --git a/e2e/cypress.config.ts b/e2e/cypress.config.ts index 9aaa4fe9d..2f1e11771 100644 --- a/e2e/cypress.config.ts +++ b/e2e/cypress.config.ts @@ -27,7 +27,8 @@ export default defineConfig({ './cypress/tests/14-*/*.ts', './cypress/tests/15-*/*.ts', './cypress/tests/16-*/*.ts', - './cypress/tests/17-*/*.ts' + './cypress/tests/17-*/*.ts', + './cypress/tests/18-*/*.ts' ] return config }, diff --git a/e2e/cypress/fixtures/state/scanID.json b/e2e/cypress/fixtures/state/scanID.json new file mode 100644 index 000000000..af5c350dd --- /dev/null +++ b/e2e/cypress/fixtures/state/scanID.json @@ -0,0 +1,4 @@ +{ + "items": [ + ] +} \ No newline at end of file diff --git a/e2e/cypress/fixtures/state/scanResult.json b/e2e/cypress/fixtures/state/scanResult.json new file mode 100644 index 000000000..e69de29bb diff --git a/e2e/cypress/support/auth-commands.ts b/e2e/cypress/support/auth-commands.ts index 65263d7ff..fd289af65 100644 --- a/e2e/cypress/support/auth-commands.ts +++ b/e2e/cypress/support/auth-commands.ts @@ -422,17 +422,51 @@ Cypress.Commands.add('setAuthorizationToken', (token: string) => { }) Cypress.Commands.add('makeAPIRequest', (endPoint: string, methodType: string) => { + let body = {} - + let requestData: any = {} if (methodType.toUpperCase() === 'PUT' || methodType.toUpperCase() === 'POST') { body = requestBody } - return cy.request({ - url: Cypress.env('BASE_URL') + '/' + endPoint, - method: methodType, - body: body, + + requestData['appname'] = "Test1" + requestData['url'] = Cypress.env('BASE_URL') + '/' + endPoint + requestData['headers'] = headers + requestData['body'] = "" + requestData['method'] = methodType + + // Scan request with Astra + cy.request({ + url: 'http://astra.localtest.me:8094/scan/', + method: 'POST', + body: requestData, headers: headers, failOnStatusCode: false + }).then((astraResponse) => { + // Actual API request + cy.request({ + url: Cypress.env('BASE_URL') + '/' + endPoint, + method: methodType, + body: body, + headers: headers, + failOnStatusCode: false + }).then((apiResponse) => { + // You can also return data or use it in further tests + const responseData = { + astraRes: astraResponse, + apiRes: apiResponse, + }; + // cy.addToAstraScanIdList(response2.body.status) + return responseData; + }) + }); +}) + +Cypress.Commands.add('makeAPIRequestForScanResult', (scanID: string) => { + return cy.request({ + url: 'http://astra.localtest.me:8094/alerts/' + scanID, + method: 'GET', + failOnStatusCode: false }) }) diff --git a/e2e/cypress/support/global.d.ts b/e2e/cypress/support/global.d.ts index 81337c99e..66c452b27 100644 --- a/e2e/cypress/support/global.d.ts +++ b/e2e/cypress/support/global.d.ts @@ -101,5 +101,11 @@ declare namespace Cypress { updateJsonBoby(json: any, key: string, newValue: string):Chainable deleteFileInE2EFolder(fileName: string):Chainable + + addToAstraScanIdList(item: any):Chainable + + checkAstraScanResultForVulnerability():Chainable + + makeAPIRequestForScanResult(scanID: string): Chainable> } } diff --git a/e2e/cypress/support/util-commands.ts b/e2e/cypress/support/util-commands.ts index b18437f2d..c42c1a009 100644 --- a/e2e/cypress/support/util-commands.ts +++ b/e2e/cypress/support/util-commands.ts @@ -170,6 +170,21 @@ Cypress.Commands.add('deleteFileInE2EFolder', (fileName: string) => { } }); +Cypress.Commands.add('addToAstraScanIdList', (item) => { + cy.readFile('cypress/fixtures/state/scanID.json').then((fileContent) => { + // Initialize the list if it doesn't exist + const items = fileContent.items || []; + + // Append the new item to the list + items.push(item); + + // Create an object with the updated list + const updatedData = { items }; + + // Write the updated object back to the file + cy.writeFile('cypress/fixtures/state/scanID.json', updatedData); + }); +}); Cypress.Commands.add('replaceWord', (originalString: string, wordToReplace: string, replacementWord: string)=> { // Create a regular expression with the 'g' flag for global search @@ -180,4 +195,34 @@ Cypress.Commands.add('replaceWord', (originalString: string, wordToReplace: stri replacedString = originalString.replace(regex, replacementWord); return replacedString; +}) + +Cypress.Commands.add('checkAstraScanResultForVulnerability', () => { + let aggregatedData = {}; + let existingData: any = []; + let flag = false + cy.readFile('cypress/fixtures/state/scanID.json').then((fileContent) => { + fileContent.items.forEach((item: string) => { + // Perform an action based on each item in the array + cy.makeAPIRequestForScanResult(item).then((response) => { + const newResponse = JSON.parse(JSON.stringify(response.body)); + existingData.push(newResponse); + }); + }); + }).then(() => { + cy.writeFile('cypress/fixtures/state/scanResult.json', JSON.stringify(existingData)) + for (var i = 0; i < existingData.length; i++) { + var jsonObject = existingData[i]; + for (var j = 0; i < jsonObject.length; i++) { + if (jsonObject[j].hasOwnProperty("impact") && + ["High", "Medium"].includes(jsonObject[j]["impact"])) { + flag = true; + } + } + } + }).then(()=>{ + if (flag){ + assert.fail("Some of the results have high or medium severity security vulnerabilities. Please check the result file for more details."); + } + }) }) \ No newline at end of file diff --git a/e2e/cypress/tests/01-api-key/01-create-api.cy.ts b/e2e/cypress/tests/01-api-key/01-create-api.cy.ts index 63d3d589e..714d916e7 100644 --- a/e2e/cypress/tests/01-api-key/01-create-api.cy.ts +++ b/e2e/cypress/tests/01-api-key/01-create-api.cy.ts @@ -102,8 +102,8 @@ it('Verify gwa gateway publish multiple config file', () => { cy.get('@api').then(({ organization }: any) => { cy.setHeaders(organization.headers) cy.setAuthorizationToken(userSession) - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) }) }) }) diff --git a/e2e/cypress/tests/01-api-key/09-gwa-get.ts b/e2e/cypress/tests/01-api-key/09-gwa-get.ts index 9e70f2a75..c8dfcdeae 100644 --- a/e2e/cypress/tests/01-api-key/09-gwa-get.ts +++ b/e2e/cypress/tests/01-api-key/09-gwa-get.ts @@ -53,8 +53,8 @@ describe('Verify GWA get commands', () => { cy.get('@api').then(({ apiDirectory }: any) => { cy.setHeaders(apiDirectory.headers) cy.setAuthorizationToken(userSession) - cy.makeAPIRequest(apiDirectory.endPoint + '/' + _namespace + '/directory', 'GET').then((res) => { - resObj = res.body[0] + cy.makeAPIRequest(apiDirectory.endPoint + '/' + _namespace + '/directory', 'GET').then((res:any) => { + resObj = res.apiRes.body[0] Cypress._.isEqual(resObj, JSON.parse(response.stdout)[0]) }) }) diff --git a/e2e/cypress/tests/02-client-credential-flow/03-client-cred-create-api-prod-auth-pro.cy.ts b/e2e/cypress/tests/02-client-credential-flow/03-client-cred-create-api-prod-auth-pro.cy.ts index bfec3de48..fe2ab6796 100644 --- a/e2e/cypress/tests/02-client-credential-flow/03-client-cred-create-api-prod-auth-pro.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/03-client-cred-create-api-prod-auth-pro.cy.ts @@ -83,8 +83,8 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t cy.get('@api').then(({ organization }: any) => { cy.setHeaders(organization.headers) cy.setAuthorizationToken(userSession) - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) }) }) }) diff --git a/e2e/cypress/tests/09-update-product-env/06-shared-idp.cy.ts b/e2e/cypress/tests/09-update-product-env/06-shared-idp.cy.ts index 6603ceab1..5f5744773 100644 --- a/e2e/cypress/tests/09-update-product-env/06-shared-idp.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/06-shared-idp.cy.ts @@ -56,9 +56,9 @@ describe('Apply Shared IDP while creating Authorization Profile', () => { it('Publish the Shared IDP profile', () => { cy.get('@common-testdata').then(({ namespace }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + namespace + '/issuers', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body.result).to.be.contain('created') + cy.makeAPIRequest('ds/api/v2/namespaces/' + namespace + '/issuers', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + expect(response.apiRes.body.result).to.be.contain('created') }) }) }) @@ -121,8 +121,8 @@ describe('Update IDP issuer for shared IDP profile', () => { it('Put the resource and verify the success code in the response', () => { cy.get('@common-testdata').then(({ namespace }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + namespace + '/issuers', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest('ds/api/v2/namespaces/' + namespace + '/issuers', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) }) }) }) diff --git a/e2e/cypress/tests/10-clear-resources/01-create-api.cy.ts b/e2e/cypress/tests/10-clear-resources/01-create-api.cy.ts index 592ec430b..d7c126c42 100644 --- a/e2e/cypress/tests/10-clear-resources/01-create-api.cy.ts +++ b/e2e/cypress/tests/10-clear-resources/01-create-api.cy.ts @@ -83,8 +83,8 @@ describe('Create API Spec for Delete Resources', () => { cy.get('@api').then(({ organization }: any) => { cy.setHeaders(organization.headers) cy.setAuthorizationToken(userSession) - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + namespace, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + namespace, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) }) }) }) diff --git a/e2e/cypress/tests/11-activity-feed/01-activity-feed.cy.ts b/e2e/cypress/tests/11-activity-feed/01-activity-feed.cy.ts index 238fdb6b7..cae68ff2d 100644 --- a/e2e/cypress/tests/11-activity-feed/01-activity-feed.cy.ts +++ b/e2e/cypress/tests/11-activity-feed/01-activity-feed.cy.ts @@ -53,9 +53,9 @@ describe('API Tests for Activity report', () => { it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity?first=100", 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity?first=100", 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body }) }) }) @@ -80,9 +80,9 @@ describe('Generate activity response from APS V2 API', () => { it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity?first=100", 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity?first=100", 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body }) }) }) diff --git a/e2e/cypress/tests/11-activity-feed/02-activity-feed-failure.cy.ts b/e2e/cypress/tests/11-activity-feed/02-activity-feed-failure.cy.ts index a9ad76d3b..74c4a2e8e 100644 --- a/e2e/cypress/tests/11-activity-feed/02-activity-feed-failure.cy.ts +++ b/e2e/cypress/tests/11-activity-feed/02-activity-feed-failure.cy.ts @@ -96,9 +96,9 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity?first=100", 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity?first=100", 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body }) }) }) diff --git a/e2e/cypress/tests/12-access-permission/01-create-api.cy.ts b/e2e/cypress/tests/12-access-permission/01-create-api.cy.ts index 5011d6b0b..938ff9685 100644 --- a/e2e/cypress/tests/12-access-permission/01-create-api.cy.ts +++ b/e2e/cypress/tests/12-access-permission/01-create-api.cy.ts @@ -81,8 +81,8 @@ describe('Create API Spec', () => { cy.get('@api').then(({ organization }: any) => { cy.setHeaders(organization.headers) cy.setAuthorizationToken(userSession) - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + namespace, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + namespace, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) }) }) }) diff --git a/e2e/cypress/tests/12-access-permission/09-content-publish.cy.ts b/e2e/cypress/tests/12-access-permission/09-content-publish.cy.ts index b607367d5..bad3c0f6b 100644 --- a/e2e/cypress/tests/12-access-permission/09-content-publish.cy.ts +++ b/e2e/cypress/tests/12-access-permission/09-content-publish.cy.ts @@ -52,9 +52,9 @@ describe('Verify Content Publish Permission', () => { it('Verify that the document is not published without "Content.Publish" permission', () => { cy.get('@api').then(({ documentation }: any) => { - cy.makeAPIRequest(documentation.endPoint, 'PUT').then((response) => { - expect(response.status).to.be.equal(401) - expect(response.body.message).contain('Missing authorization scope') + cy.makeAPIRequest(documentation.endPoint, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(401) + expect(response.apiRes.body.message).contain('Missing authorization scope') }) }) }) diff --git a/e2e/cypress/tests/15-aps-api/01-create-api.cy.ts b/e2e/cypress/tests/15-aps-api/01-create-api.cy.ts index 1d9b664e4..7dd135961 100644 --- a/e2e/cypress/tests/15-aps-api/01-create-api.cy.ts +++ b/e2e/cypress/tests/15-aps-api/01-create-api.cy.ts @@ -60,8 +60,8 @@ describe('Create API Spec', () => { cy.get('@api').then(({ organization }: any) => { cy.setHeaders(organization.headers) cy.setAuthorizationToken(userSession) - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + namespace, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + namespace, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) }) }) }) diff --git a/e2e/cypress/tests/15-aps-api/02-organization.cy.ts b/e2e/cypress/tests/15-aps-api/02-organization.cy.ts index 12ab1caf6..989a099f5 100644 --- a/e2e/cypress/tests/15-aps-api/02-organization.cy.ts +++ b/e2e/cypress/tests/15-aps-api/02-organization.cy.ts @@ -31,7 +31,6 @@ describe('Get the user session token', () => { }) }) - describe('API Tests to verify the Organization details in the response', () => { beforeEach(() => { @@ -48,11 +47,12 @@ describe('API Tests to verify the Organization details in the response', () => { it('Get the resource and verify the Organization details in the response', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint, 'GET').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body[0].name).to.eq("ministry-of-health") - expect(response.body[0].title).to.eq("Ministry of Health") - expect(response.body[0]).has.property('title', 'Ministry of Health') + cy.makeAPIRequest(organization.endPoint, 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + expect(response.apiRes.body[0].name).to.eq("ministry-of-health") + expect(response.apiRes.body[0].title).to.eq("Ministry of Health") + expect(response.apiRes.body[0]).has.property('title', 'Ministry of Health') + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -74,24 +74,24 @@ describe('Verify /Organization/{Org} end point', () => { it('Get the resource and verify the org Names in the response', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName, 'GET').then((response) => { - expect(response.status).to.be.equal(200) - assert.isTrue(Cypress._.isEqual(response.body.orgUnits[0], organization.orgExpectedList)) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName, 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + assert.isTrue(Cypress._.isEqual(response.apiRes.body.orgUnits[0], organization.orgExpectedList)) }) }) }) it('Verify the status code and response message for invalid organization name', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/health', 'GET').then((response) => { - expect(response.status).to.be.oneOf([404, 422]) - expect(response.body.message).to.be.equal("Validation Failed") + cy.makeAPIRequest(organization.endPoint + '/health', 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.oneOf([404, 422]) + expect(response.apiRes.body.message).to.be.equal("Validation Failed") }) }) }) }) - describe('Get the Organization Role', () => { var response: any @@ -111,9 +111,10 @@ describe('Get the Organization Role', () => { it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/roles', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/roles', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) @@ -127,9 +128,10 @@ describe('Get the Organization Role', () => { }) it('Get the list of roles and verify the success code in the response', () => { - cy.makeAPIRequest('ds/api/v2/roles', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest('ds/api/v2/roles', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body + cy.addToAstraScanIdList(res.astraRes.body.status) }) }) @@ -161,9 +163,10 @@ describe('Get the Namespace associated with the organization', () => { it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/namespaces', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/namespaces', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body nameSpace = response[0].name }) }) @@ -201,9 +204,10 @@ describe('Delete the Namespace associated with the organization', () => { it('Delete the namespace associated with the organization, organization unit and verify the success code in the response', () => { cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'DELETE').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'DELETE').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) @@ -211,9 +215,10 @@ describe('Delete the Namespace associated with the organization', () => { it('Verify that the deleted Namespace is not displayed in Get Call', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/namespaces', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/namespaces', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body assert.equal(response.findIndex((x: { name: string }) => x.name === nameSpace), -1) }) }) @@ -241,17 +246,19 @@ describe('Add and Get Organization Access', () => { it('Add the access of the organization to the specific user and verify the success code in the response', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/access', 'PUT').then((res) => { - expect(res.status).to.be.equal(204) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/access', 'PUT').then((res:any) => { + expect(res.apiRes.status).to.be.equal(204) + cy.addToAstraScanIdList(res.astraRes.body.status) }) }) }) it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ organization }: any) => { - cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/access', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/access', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) @@ -264,5 +271,7 @@ describe('Add and Get Organization Access', () => { after(() => { cy.logout() + cy.clearLocalStorage({ log: true }) + cy.deleteAllCookies() }) }) \ No newline at end of file diff --git a/e2e/cypress/tests/15-aps-api/03-documentation.cy.ts b/e2e/cypress/tests/15-aps-api/03-documentation.cy.ts index a0428633f..08b39b41d 100644 --- a/e2e/cypress/tests/15-aps-api/03-documentation.cy.ts +++ b/e2e/cypress/tests/15-aps-api/03-documentation.cy.ts @@ -53,8 +53,9 @@ describe('API Tests for Updating documentation', () => { cy.get('@api').then(({ documentation }: any) => { cy.replaceWord(documentation.endPoint, 'apiplatform', namespace).then((updatedEndPoint: string) => { updatedDocumentEndPoint = updatedEndPoint - cy.makeAPIRequest(updatedDocumentEndPoint, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedDocumentEndPoint, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -80,10 +81,11 @@ describe('API Tests for Fetching documentation', () => { }) it('Get the resource and verify the success code in the response', () => { - cy.makeAPIRequest(updatedDocumentEndPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - slugValue = res.body[0].slug - response = res.body[0] + cy.makeAPIRequest(updatedDocumentEndPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + slugValue = res.apiRes.body[0].slug + response = res.apiRes.body[0] }) }) @@ -113,16 +115,18 @@ describe('API Tests for Deleting documentation', () => { }) it('Verify the status code and response message for invalid slugvalue', () => { - cy.makeAPIRequest(updatedDocumentEndPoint + '/platform_test', 'DELETE').then((response) => { - expect(response.status).to.be.oneOf([404, 422]) - expect(response.body.message).to.be.equal("Content not found") + cy.makeAPIRequest(updatedDocumentEndPoint + '/platform_test', 'DELETE').then((response:any) => { + expect(response.apiRes.status).to.be.oneOf([404, 422]) + expect(response.apiRes.body.message).to.be.equal("Content not found") + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) it('Delete the documentation', () => { - cy.makeAPIRequest(updatedDocumentEndPoint + '/' + slugValue, 'DELETE').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedDocumentEndPoint + '/' + slugValue, 'DELETE').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -145,9 +149,10 @@ describe('API Tests to verify no value in Get call after deleting document conte }) it('Delete the documentation', () => { - cy.makeAPIRequest(updatedDocumentEndPoint, 'GET').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body).to.be.empty + cy.makeAPIRequest(updatedDocumentEndPoint, 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body).to.be.empty }) }) }) @@ -179,8 +184,9 @@ describe('API Tests to verify Get documentation content', () => { }) it('Put the resource and verify the success code in the response', () => { - cy.makeAPIRequest(updatedDocumentEndPoint, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedDocumentEndPoint, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) @@ -193,30 +199,33 @@ describe('API Tests to verify Get documentation content', () => { it('Verify that document contant is displayed for GET /documentation', () => { cy.get('@api').then(({ documentation }: any) => { - cy.makeAPIRequest(documentation.getDocumentation_endPoint, 'GET').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body[0].title).to.be.equal(documentation.body.title) - expect(response.body[0].description).to.be.equal(documentation.body.description) - slugID = response.body[0].slug + cy.makeAPIRequest(documentation.getDocumentation_endPoint, 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body[0].title).to.be.equal(documentation.body.title) + expect(response.apiRes.body[0].description).to.be.equal(documentation.body.description) + slugID = response.apiRes.body[0].slug }) }) }) it('Verify the status code and response message for invalid slug id', () => { cy.get('@api').then(({ documentation }: any) => { - cy.makeAPIRequest(documentation.getDocumentation_endPoint + '/998898', 'GET').then((response) => { - expect(response.status).to.be.oneOf([404, 422]) - expect(response.body.message).to.be.contains("Not Found") + cy.makeAPIRequest(documentation.getDocumentation_endPoint + '/998898', 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.oneOf([404, 422]) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body.message).to.be.contains("Not Found") }) }) }) it('Verify that document contant is fetch by slug ID', () => { cy.get('@api').then(({ documentation }: any) => { - cy.makeAPIRequest(documentation.getDocumentation_endPoint + '/' + slugID, 'GET').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body.slug).to.be.equal(slugID) - expect(response.body.title).to.be.equal(documentation.body.title) + cy.makeAPIRequest(documentation.getDocumentation_endPoint + '/' + slugID, 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body.slug).to.be.equal(slugID) + expect(response.apiRes.body.title).to.be.equal(documentation.body.title) }) }) }) diff --git a/e2e/cypress/tests/15-aps-api/05-authorizationProfiles.cy.ts b/e2e/cypress/tests/15-aps-api/05-authorizationProfiles.cy.ts index d60901552..c490f0ca1 100644 --- a/e2e/cypress/tests/15-aps-api/05-authorizationProfiles.cy.ts +++ b/e2e/cypress/tests/15-aps-api/05-authorizationProfiles.cy.ts @@ -64,17 +64,19 @@ testData.forEach((testCase: any) => { cy.get('@api').then(({ authorizationProfiles }: any) => { cy.replaceWord(authorizationProfiles.endPoint, 'apiplatform', namespace).then((updatedEndPoint: string) => { updatedAuthProfileEndPoint = updatedEndPoint - cy.makeAPIRequest(updatedAuthProfileEndPoint, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedAuthProfileEndPoint, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) }) it('Get the resource and verify the success code in the response', () => { - cy.makeAPIRequest(updatedAuthProfileEndPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(updatedAuthProfileEndPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) @@ -87,16 +89,18 @@ testData.forEach((testCase: any) => { }) it('Delete the authorization profile', () => { - cy.makeAPIRequest(updatedAuthProfileEndPoint + '/' + testCase.body.name, 'DELETE').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedAuthProfileEndPoint + '/' + testCase.body.name, 'DELETE').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) it('Verify that the authorization profile is deleted', () => { - cy.makeAPIRequest(updatedAuthProfileEndPoint, 'GET').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body.length).to.be.equal(0) + cy.makeAPIRequest(updatedAuthProfileEndPoint, 'GET').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body.length).to.be.equal(0) }) }) }) @@ -124,8 +128,9 @@ describe('API Tests for Authorization Profiles created with inheritFrom attribut it('Put the resource to create shared IDP profile and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -140,18 +145,20 @@ describe('API Tests for Authorization Profiles created with inheritFrom attribut it('Create an authorization profile using inheritFrom attribute and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body.result).to.be.equal("created") + cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body.result).to.be.equal("created") }) }) }) it('Get list of authorization profile and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) @@ -188,18 +195,20 @@ describe('Published a shared authorization profile', () => { it('Create a shared credential issuer', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) - expect(response.body.result).to.be.equal("created") + cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) + expect(response.apiRes.body.result).to.be.equal("created") }) }) }) it('Get list of authorization profile and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest('ds/api/v2/namespaces/' + apiTest.namespace + '/issuers', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) diff --git a/e2e/cypress/tests/15-aps-api/06-products.cy.ts b/e2e/cypress/tests/15-aps-api/06-products.cy.ts index 453063c2f..e169e786e 100644 --- a/e2e/cypress/tests/15-aps-api/06-products.cy.ts +++ b/e2e/cypress/tests/15-aps-api/06-products.cy.ts @@ -64,8 +64,9 @@ describe('API Tests for Updating Products', () => { cy.get('@api').then(({ products }: any) => { cy.replaceWord(products.endPoint, 'apiplatform', namespace).then((updatedEndPoint: string) => { updatedProductEndPoint = updatedEndPoint - cy.makeAPIRequest(updatedProductEndPoint, 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedProductEndPoint, 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -73,12 +74,12 @@ describe('API Tests for Updating Products', () => { it('Get the resource and verify the success code and product name in the response', () => { cy.get('@api').then(({ products }: any) => { - cy.makeAPIRequest(updatedProductEndPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - let index = res.body.findIndex((x: { name: string }) => x.name === products.body.name) - response = res.body[index] - productID = res.body[index].appId - envID = res.body[index].environments[0].appId + cy.makeAPIRequest(updatedProductEndPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + let index = res.apiRes.body.findIndex((x: { name: string }) => x.name === products.body.name) + response = res.apiRes.body[index] + productID = res.apiRes.body[index].appId + envID = res.apiRes.body[index].environments[0].appId }) }) }) @@ -136,8 +137,9 @@ describe('API Tests for Delete Products', () => { it('Delete the product environment and verify the success code in the response', () => { cy.get('@api').then(({ products }: any) => { cy.replaceWord(products.deleteEnvironmentEndPoint, 'apiplatform', namespace).then((updatedEndPoint: string) => { - cy.makeAPIRequest(updatedEndPoint + '/' + envID, 'Delete').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedEndPoint + '/' + envID, 'Delete').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -145,25 +147,28 @@ describe('API Tests for Delete Products', () => { it('Get the resource and verify that product environment is deleted', () => { cy.get('@api').then(({ products }: any) => { - cy.makeAPIRequest(updatedProductEndPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - let index = res.body.findIndex((x: { name: string }) => x.name === products.body.name) - expect(res.body[index].environments).to.be.empty + cy.makeAPIRequest(updatedProductEndPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + let index = res.apiRes.body.findIndex((x: { name: string }) => x.name === products.body.name) + expect(res.apiRes.body[index].environments).to.be.empty }) }) }) it('Delete the product and verify the success code in the response', () => { - cy.makeAPIRequest(updatedProductEndPoint + '/' + productID, 'Delete').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(updatedProductEndPoint + '/' + productID, 'Delete').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) it('Get the resource and verify that product is deleted', () => { cy.get('@api').then(({ products }: any) => { - cy.makeAPIRequest(updatedProductEndPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(updatedProductEndPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body + cy.addToAstraScanIdList(res.astraRes.body.status) assert.equal(response.findIndex((x: { name: string }) => x.name === products.body.name), -1) }) }) diff --git a/e2e/cypress/tests/15-aps-api/07-api-directory.cy.ts b/e2e/cypress/tests/15-aps-api/07-api-directory.cy.ts index eec37feeb..7f235b2f7 100644 --- a/e2e/cypress/tests/15-aps-api/07-api-directory.cy.ts +++ b/e2e/cypress/tests/15-aps-api/07-api-directory.cy.ts @@ -55,8 +55,9 @@ describe('API Tests for Updating dataset', () => { it('Put the resource (/organization/{org}/datasets) and verify the success code in the response', () => { cy.get('@api').then(({ apiDirectory, organization }: any) => { - cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -64,9 +65,9 @@ describe('API Tests for Updating dataset', () => { it('Get the resource (/organization/{org}/datasets/{name}) and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/datasets/' + apiDirectory.body.name, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/datasets/' + apiDirectory.body.name, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body }) }) }) @@ -81,8 +82,9 @@ describe('API Tests for Updating dataset', () => { it('Put the resource (/namespaces/{ns}/datasets/{name}) and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/datasets', 'PUT').then((response) => { - expect(response.status).to.be.equal(200) + cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/datasets', 'PUT').then((response:any) => { + expect(response.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(response.astraRes.body.status) }) }) }) @@ -91,9 +93,9 @@ describe('API Tests for Updating dataset', () => { it('Get the resource (/namespaces/{ns}/datasets/{name}) and verify the success code in the response', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/datasets/' + apiDirectory.body.name, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/datasets/' + apiDirectory.body.name, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body }) }) }) @@ -107,9 +109,9 @@ describe('API Tests for Updating dataset', () => { it('Get the resource (/organizations/{org}/datasets/{name}) and verify the success code in the response', () => { cy.get('@api').then(({ apiDirectory, organization }: any) => { - cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/' + apiDirectory.body.name, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/' + apiDirectory.body.name, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body }) }) }) @@ -122,9 +124,10 @@ describe('API Tests for Updating dataset', () => { it('Get the resource (/organizations/{org}/datasets) and verify the success code in the response', () => { cy.get('@api').then(({ apiDirectory, organization }: any) => { - cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) @@ -137,11 +140,12 @@ describe('API Tests for Updating dataset', () => { it('Get the directory details (/directory) and verify the success code in the response', () => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.directoryEndPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body - directoryID = res.body[0].id - directoryName = res.body[0].name + cy.makeAPIRequest(apiDirectory.directoryEndPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body + directoryID = res.apiRes.body[0].id + directoryName = res.apiRes.body[0].name }) }) }) @@ -154,9 +158,10 @@ describe('API Tests for Updating dataset', () => { it('Get the directory details by its ID (/directory/{id}) and verify the success code in the response', () => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.directoryEndPoint + '/' + directoryID, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body.name).to.be.equal(directoryName) + cy.makeAPIRequest(apiDirectory.directoryEndPoint + '/' + directoryID, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body.name).to.be.equal(directoryName) }) }) }) @@ -164,9 +169,10 @@ describe('API Tests for Updating dataset', () => { it('Get the namespace directory details (/namespaces/{ns}/directory) and verify the success code and empty response for the namespace with no directory', () => { cy.get('@common-testdata').then(({ apiTest }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/directory', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body).to.be.empty + cy.makeAPIRequest(apiDirectory.endPoint + '/' + apiTest.namespace + '/directory', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body).to.be.empty }) }) }) @@ -175,11 +181,11 @@ describe('API Tests for Updating dataset', () => { it('Get the namespace directory details (/namespaces/{ns}/directory) and verify the success code in the response', () => { cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + namespace + '/directory', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body[0] - directoryID = res.body[0].id - directoryName = res.body[0].name + cy.makeAPIRequest(apiDirectory.endPoint + '/' + namespace + '/directory', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + response = res.apiRes.body[0] + directoryID = res.apiRes.body[0].id + directoryName = res.apiRes.body[0].name }) }) }) @@ -194,9 +200,10 @@ describe('API Tests for Updating dataset', () => { it('Get the namespace directory details by its ID (/namespaces/{ns}/directory/{id}) and verify the success code in the response', () => { cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + namespace + '/directory' + '/' + directoryID, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body.name).to.be.equal(directoryName) + cy.makeAPIRequest(apiDirectory.endPoint + '/' + namespace + '/directory' + '/' + directoryID, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body.name).to.be.equal(directoryName) }) }) }) @@ -205,8 +212,9 @@ describe('API Tests for Updating dataset', () => { it('Get the namespace directory details (/namespaces/{ns}/directory/{id}) for non exist directory ID and verify the response code', () => { cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@api').then(({ apiDirectory }: any) => { - cy.makeAPIRequest(apiDirectory.endPoint + '/' + namespace + '/directory' + '/99', 'GET').then((res) => { - expect(res.status).to.be.oneOf([404, 422]) + cy.makeAPIRequest(apiDirectory.endPoint + '/' + namespace + '/directory' + '/99', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.oneOf([404, 422]) + cy.addToAstraScanIdList(res.astraRes.body.status) }) }) }) @@ -214,17 +222,19 @@ describe('API Tests for Updating dataset', () => { it('Delete the dataset (/organizations/{org}/datasets/{name}) and verify the success code in the response', () => { cy.get('@api').then(({ apiDirectory, organization }: any) => { - cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/' + apiDirectory.body.name, 'DELETE').then((res) => { - expect(res.status).to.be.equal(200) + cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/' + apiDirectory.body.name, 'DELETE').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) }) }) }) it('Verify that deleted dataset does not display in Get dataset list', () => { cy.get('@api').then(({ apiDirectory, organization }: any) => { - cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/', 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(apiDirectory.orgEndPoint + '/' + organization.orgName + '/datasets/', 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body expect(response).to.not.contain(apiDirectory.body.name) }) }) diff --git a/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts b/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts index 6d3363627..433f5fa86 100644 --- a/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts +++ b/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts @@ -77,9 +77,10 @@ describe('API Tests for Namespace List', () => { it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body + cy.makeAPIRequest(namespaces.endPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body }) }) }) @@ -108,8 +109,9 @@ describe('API Tests for Namespace Activities', () => { it('Get the resource and verify the success code in the response', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity", 'GET').then((res) => { - expect(res.status).to.be.equal(200) + cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace + "/activity", 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) }) }) }) @@ -137,9 +139,10 @@ describe('API Tests for Namespace Summary', () => { it('Get the resource for namespace summary and verify the success code in the response', () => { cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint + "/" + namespace, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - response = res.body.name + cy.makeAPIRequest(namespaces.endPoint + "/" + namespace, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + response = res.apiRes.body.name }) }) }) @@ -172,19 +175,21 @@ describe('API Tests for Create Namespace', () => { it('Create system generated namespace when user does not specify namespace name', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body.displayName).to.be.equal(null) - nameSpace = res.body.name + cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body.displayName).to.be.equal(null) + nameSpace = res.apiRes.body.name }) }) }) it('Verify that the generated namespace is displayed in the namespace list', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body).to.be.contain(nameSpace) + cy.makeAPIRequest(namespaces.endPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body).to.be.contain(nameSpace) }) }) }) @@ -192,19 +197,21 @@ describe('API Tests for Create Namespace', () => { it('Create users own namespace with its description', () => { cy.get('@api').then(({ namespaces }: any) => { cy.setRequestBody(namespaces.userDefinedNamespace) - cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body.displayName).to.be.equal(namespaces.userDefinedNamespace.displayName) - nameSpace = res.body.name + cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body.displayName).to.be.equal(namespaces.userDefinedNamespace.displayName) + nameSpace = res.apiRes.body.name }) }) }) it('Verify that the generated namespace is displayed in the namespace list', () => { cy.get('@api').then(({ namespaces }: any) => { - cy.makeAPIRequest(namespaces.endPoint, 'GET').then((res) => { - expect(res.status).to.be.equal(200) - expect(res.body).to.be.contain(nameSpace) + cy.makeAPIRequest(namespaces.endPoint, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(200) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body).to.be.contain(nameSpace) }) }) }) @@ -227,9 +234,10 @@ describe('API Tests for invalid namespace name', () => { // cy.setRequestBody('{"name": "' + name + '","displayName": "Test for GWA test"}') cy.updateJsonBoby(namespaces.inValidNamespace, 'name', name).then((updatedBody) => { cy.setRequestBody(updatedBody) - cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res) => { - expect(res.status).to.be.equal(422) - expect(res.body.message).to.be.equal('Validation Failed') + cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res:any) => { + expect(res.apiRes.status).to.be.equal(422) + cy.addToAstraScanIdList(res.astraRes.body.status) + expect(res.apiRes.body.message).to.be.equal('Validation Failed') }) }) }) diff --git a/e2e/cypress/tests/18-scan-astra-result/01-store-and-scan-astra-result.ts b/e2e/cypress/tests/18-scan-astra-result/01-store-and-scan-astra-result.ts new file mode 100644 index 000000000..ae8d5dd78 --- /dev/null +++ b/e2e/cypress/tests/18-scan-astra-result/01-store-and-scan-astra-result.ts @@ -0,0 +1,5 @@ +describe('Store scan result and scan Astra result', () => { + it('Store astra acan result and fail the step for any unexpected security vulnerabilities', () => { + cy.checkAstraScanResultForVulnerability() + }) +}) \ No newline at end of file diff --git a/local/astra/Dockerfile b/local/astra/Dockerfile new file mode 100644 index 000000000..e4f555ff5 --- /dev/null +++ b/local/astra/Dockerfile @@ -0,0 +1,27 @@ +FROM python:2-alpine + +ENV app /app + +RUN mkdir $app +WORKDIR $app + +RUN apk update && apk add git + +RUN git clone -b dev https://github.com/flipkart-incubator/Astra.git . + +COPY . $app + +#COPY db.py utils/db.py + +#COPY dbconnection.py API/dbconnection.py + + +RUN pip install -r requirements.txt + +WORKDIR API/ + +#COPY dbconnection.py API/ + +EXPOSE 8094 +ENTRYPOINT ["python", "./api.py"] +CMD ["tail -f /app/logs/scan.log"] \ No newline at end of file diff --git a/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-339568.gql b/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-339568.gql index 02c8bfd69..3b70f716e 100644 --- a/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-339568.gql +++ b/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-339568.gql @@ -1,7 +1,9 @@ query GetCurrentNamespace { currentNamespace { + id name + displayName org orgUnit orgUpdatedAt diff --git a/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-b2df18.gql b/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-b2df18.gql index ae6c0abe6..fa72e9122 100644 --- a/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-b2df18.gql +++ b/src/authz/graphql-whitelist/httplocalhost4180managernamespaces-b2df18.gql @@ -3,6 +3,7 @@ allNamespaces { id name + displayName orgEnabled orgUpdatedAt } diff --git a/src/authz/graphql-whitelist/httpoauth2proxylocaltestme4180managernamespaces-a5ae8c.gql b/src/authz/graphql-whitelist/httpoauth2proxylocaltestme4180managernamespaces-a5ae8c.gql new file mode 100644 index 000000000..65ad9f325 --- /dev/null +++ b/src/authz/graphql-whitelist/httpoauth2proxylocaltestme4180managernamespaces-a5ae8c.gql @@ -0,0 +1,4 @@ + + mutation UpdateNamespaceDisplayName($displayName: String!) { + updateCurrentNamespaceDisplayName(displayName: $displayName) + } diff --git a/src/authz/matrix.csv b/src/authz/matrix.csv index 718639ecd..deb87f3ae 100644 --- a/src/authz/matrix.csv +++ b/src/authz/matrix.csv @@ -128,6 +128,7 @@ API Owner Role Rules,,forceDeleteNamespace,,,,,,,api-owner,,,allow, API Owner Role Rules,,namespace,,,,,,,api-owner,,,allow, API Owner Role Rules,,currentNamespace,,,,,,,,,"portal-user,api-owner,provider-user,access-manager,credential-admin",allow, API Owner / Provider Role Rules,,updateCurrentNamespace,,,,,,,api-owner,,,allow, +API Owner / Provider Role Rules,,updateCurrentNamespaceDisplayName,,,,,,,api-owner,,,allow, API Owner Role Rules,,updatePermissions,,,,,,,api-owner,,,allow, API Owner Role Rules,,grantPermissions,,,,,,,api-owner,,,allow, API Owner Role Rules,,revokePermissions,,,,,,,api-owner,,,allow, diff --git a/src/lists/extensions/Namespace.ts b/src/lists/extensions/Namespace.ts index c680dd23b..6209d7a1b 100644 --- a/src/lists/extensions/Namespace.ts +++ b/src/lists/extensions/Namespace.ts @@ -144,6 +144,7 @@ module.exports = { const resource: any = await getResource(selectedNS, envCtx); merged['id'] = resource['id']; merged['scopes'] = resource['scopes']; + merged['displayName'] = resource['displayName']; } if (merged.org) { @@ -341,6 +342,39 @@ module.exports = { return true; }, }, + { + schema: + 'updateCurrentNamespaceDisplayName(displayName: String): String', + resolver: async ( + item: any, + { displayName }: any, + context: any, + info: any, + { query, access }: any + ): Promise => { + if ( + context.req.user?.namespace == null || + typeof context.req.user?.namespace === 'undefined' + ) { + return null; + } + + const ns = context.req.user?.namespace; + + const prodEnv = await getGwaProductEnvironment(context, true); + + await getNamespaceResourceSets(prodEnv); // sets accessToken + + const resourcesApi = new UMAResourceRegistrationService( + prodEnv.uma2.resource_registration_endpoint, + prodEnv.accessToken + ); + + await resourcesApi.updateDisplayName(ns, displayName); + return true; + }, + access: EnforcementPoint, + }, { schema: 'updateCurrentNamespace(org: String, orgUnit: String): String', @@ -416,6 +450,7 @@ module.exports = { }); } }, + access: EnforcementPoint, }, { schema: diff --git a/src/mocks/handlers.js b/src/mocks/handlers.js index 40ec6ddc8..67fecb014 100644 --- a/src/mocks/handlers.js +++ b/src/mocks/handlers.js @@ -89,12 +89,14 @@ const allNamespaces = [ { id: 'n1', name: 'aps-portal', + displayName: 'API Services Portal gw', orgEnabled: true, createdAt: subDays(new Date(), 20).toISOString(), }, { id: 'n2', name: 'loc', + displayName: 'Location services', orgEnabled: false, createdAt: subDays(new Date(), 5).toISOString(), }, diff --git a/src/mocks/resolvers/namespace-access.js b/src/mocks/resolvers/namespace-access.js index b3828a257..f561fa06d 100644 --- a/src/mocks/resolvers/namespace-access.js +++ b/src/mocks/resolvers/namespace-access.js @@ -131,6 +131,7 @@ let umaPolicies = [ let currentNamespace = { id: 'ns1', name: 'aps-portal', + displayName: 'API Services Portal gw', scopes: [ { name: 'GatewayConfig.Publish' }, { name: 'Namespace.Manage' }, @@ -172,6 +173,7 @@ export const updateCurrentNamesSpaceHandler = (req, res, ctx) => { ...req.variables, org: req.variables.org ? { title: req.variables.org } : null, orgUnit: req.variables.orgUnit ? { title: req.variables.orgUnit } : null, + displayName: req.variables.displayName ? { title: req.variables.displayName } : null, }; return res(ctx.data({})); }; diff --git a/src/nextapp/components/auth-action/auth-action.tsx b/src/nextapp/components/auth-action/auth-action.tsx index db3483799..9bf11837d 100644 --- a/src/nextapp/components/auth-action/auth-action.tsx +++ b/src/nextapp/components/auth-action/auth-action.tsx @@ -24,7 +24,6 @@ import { makeRedirectUrl, useAuth, } from '@/shared/services/auth'; -import NamespaceMenu from '../namespace-menu'; import HelpMenu from './help-menu'; import NextLink from 'next/link'; import { useGlobal } from '@/shared/services/global'; @@ -67,7 +66,6 @@ const Signin: React.FC = ({ site }) => { return ( - = ({ site }) => { return ( - } spacing={4} > - {user.roles.includes('portal-user') && } = ({ + data, + queryKey, +}) => { + const toast = useToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const queryClient = useQueryClient(); + const mutate = useApiMutation(mutation); + const [inputValue, setInputValue] = React.useState(data.displayName || ''); + const [charCount, setCharCount] = React.useState( + data.displayName?.length || 0 + ); + const charLimit = 30; + const handleInputChange = (event) => { + const { value } = event.target; + setInputValue(value); + setCharCount(value.length); + }; + const form = React.useRef(); + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + if (charCount <= charLimit) { + updateNamespaceDisplayName(); + submitTheForm(); + } + }; + const submitTheForm = async () => { + toast({ + title: 'Submitted it!', + status: 'success', + isClosable: true, + }); + }; + const updateNamespaceDisplayName = async () => { + if (form.current) { + try { + if (form.current.checkValidity()) { + const formData = new FormData(form.current); + const entries = Object.fromEntries(formData); + await mutate.mutateAsync(entries); + queryClient.invalidateQueries(queryKey); + onClose(); + toast({ + title: 'Display name successfully edited', + status: 'success', + isClosable: true, + }); + } + } catch (err) { + toast({ + title: 'Display name update failed', + description: err, + status: 'error', + isClosable: true, + }); + } + } + }; + const handleSaveClick = () => { + form.current?.requestSubmit(); + }; + return ( + <> + + + + + Edit display name + +
+ + + + A meaningful display name makes it easy for anyone to identify + and distinguish this Gateway from others. + + charLimit ? 'bc-error' : 'gray.500'} + mt={2} + textAlign="right" + > + {charCount}/{charLimit} + + charLimit} + data-testid="edit-display-name-input" + /> + {charCount > charLimit && ( + + You have reached the character limit + + )} + +
+
+ + + + + + +
+
+ + ); +}; + +export default EditNamespaceDisplayName; + +const mutation = gql` + mutation UpdateNamespaceDisplayName($displayName: String!) { + updateCurrentNamespaceDisplayName(displayName: $displayName) + } +`; diff --git a/src/nextapp/components/edit-display-name/index.ts b/src/nextapp/components/edit-display-name/index.ts new file mode 100644 index 000000000..ab9c667a2 --- /dev/null +++ b/src/nextapp/components/edit-display-name/index.ts @@ -0,0 +1 @@ +export { default } from './edit-display-name'; diff --git a/src/nextapp/components/namespace-menu/namespace-menu.tsx b/src/nextapp/components/namespace-menu/namespace-menu.tsx index 68a61f7ed..15fb7e7e1 100644 --- a/src/nextapp/components/namespace-menu/namespace-menu.tsx +++ b/src/nextapp/components/namespace-menu/namespace-menu.tsx @@ -1,7 +1,9 @@ import { UserData } from '@/types'; import { Box, + Flex, Icon, + Link, Menu, MenuButton, MenuDivider, @@ -13,9 +15,10 @@ import { useToast, } from '@chakra-ui/react'; import * as React from 'react'; -import { FaChevronDown } from 'react-icons/fa'; +import { FaChevronDown, FaServer } from 'react-icons/fa'; import { useQueryClient } from 'react-query'; import { gql } from 'graphql-request'; +import SearchInput from '@/components/search-input'; import { restApi, useApi } from '@/shared/services/api'; import { differenceInDays } from 'date-fns'; import { Namespace } from '@/shared/types/query.types'; @@ -25,17 +28,16 @@ import NewNamespace from '../new-namespace'; interface NamespaceMenuProps { user: UserData; - variant?: string; buttonMessage?: string; } const NamespaceMenu: React.FC = ({ user, - variant, buttonMessage, }) => { const client = useQueryClient(); const toast = useToast(); + const [search, setSearch] = React.useState(''); const newNamespaceDisclosure = useDisclosure(); const managerDisclosure = useDisclosure(); const { data, isLoading, isSuccess, isError } = useApi( @@ -44,6 +46,33 @@ const NamespaceMenu: React.FC = ({ { suspense: false } ); const today = new Date(); + const namespacesRecentlyViewed = JSON.parse(localStorage.getItem('namespacesRecentlyViewed') || '[]'); + + const recentNamespaces = data?.allNamespaces + .filter((namespace: Namespace) => { + const recentNamespace = namespacesRecentlyViewed.find((ns: any) => ns.namespace === namespace.name); + return recentNamespace && namespace.name !== user.namespace; + }) + .sort((a, b) => { + const aRecent = namespacesRecentlyViewed.find((ns: any) => ns.namespace === a.name); + const bRecent = namespacesRecentlyViewed.find((ns: any) => ns.namespace === b.name); + return new Date(bRecent.updatedAt).getTime() - new Date(aRecent.updatedAt).getTime(); + }) + .slice(0, 5); + const handleSearchChange = (value: string) => { + setSearch(value); + }; + const namespaceSearchResults = React.useMemo(() => { + const result = + data?.allNamespaces ?? []; + if (search.trim()) { + const regex = new RegExp( + search.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + ); + return result.filter((s) => s.name.search(regex) >= 0); + } + return result; + }, [data, search]); const handleNamespaceChange = React.useCallback( (namespace: Namespace) => async () => { @@ -54,6 +83,18 @@ const NamespaceMenu: React.FC = ({ }); try { await restApi(`/admin/switch/${namespace.id}`, { method: 'PUT' }); + const existingEntryIndex = namespacesRecentlyViewed.findIndex((entry: any) => entry.userId === user.userId && entry.namespace === user.namespace); + if (existingEntryIndex !== -1) { + namespacesRecentlyViewed[existingEntryIndex].updatedAt = user.updatedAt; + } else { + namespacesRecentlyViewed.push({ + userId: user.userId, + namespace: user.namespace, + updatedAt: user.updatedAt + }); + } + localStorage.setItem('namespacesRecentlyViewed', JSON.stringify(namespacesRecentlyViewed)); + handleSearchChange(''); toast.closeAll(); client.invalidateQueries(); toast({ @@ -70,31 +111,40 @@ const NamespaceMenu: React.FC = ({ }); } }, - [client, toast] + [client, toast, user] ); - const isNamespaceSelector = variant === 'ns-selector'; - return ( <> - {user?.namespace ?? buttonMessage ?? 'No Active Namespace'}{' '} - + + {user?.namespace ?? buttonMessage ?? 'No Active Gateway'} + + = ({ }, }} > - <> + + + event.currentTarget.focus()} + onChange={handleSearchChange} + value={search} + data-testid="namespace-search-input" + /> + {isLoading && Loading namespaces...} {isError && ( Namespaces Failed to Load )} {isSuccess && data.allNamespaces.length > 0 && ( - - - {data.allNamespaces - .filter((n) => n.name !== user.namespace) - .sort((a, b) => a.name.localeCompare(b.name)) - .map((n) => ( + <> + + + {namespaceSearchResults.length} result{namespaceSearchResults.length !== 1 ? 's' : ''} for "{search}" + + ) + : ( + + Recently viewed + + ) + } + > + {(search !== '' && namespaceSearchResults.length === 0) && ( + + Empty folder + + )} + {(search !== '' ? namespaceSearchResults : recentNamespaces).map((n) => ( = ({ flexDir="column" alignItems="flex-start" pos="relative" + pl={6} + pt={2} + pb={2} > - {differenceInDays(today, new Date(n.orgUpdatedAt)) <= - 5 && ( - - New - - )} - {n.name} - { - /* @ts-ignore */ - !n.orgEnabled && ( - - API Publishing Disabled - - ) - } + + + {n.displayName ? n.displayName : `Gateway ${n.name}`} + + {n.name} - ))} - - + ))} + + + + + {`You have ${data.allNamespaces.length} Gateway${ + data.allNamespaces.length !== 1 ? 's' : '' + } in total`} + + + Go to the full Gateways list + + + )} - - {!isNamespaceSelector && ( - <> + + {/* TODO: Remove. This is here for convenience for creating NS's during prelim testing. */} + {/* <> - + = ({ > Create New Namespace - - Export Namespace Report - - - )} + */} = ({ site, links, pathname }) => { return ''; }, [pathname]); - + return ( - - + - {authenticatedLinks.map(({ BadgeElement, ...link }) => ( - - - - - {link.name} - - {BadgeElement && ( - - + + {authenticatedLinks.map(({ BadgeElement, ...link }) => ( + + + + + {link.name} - )} - - - - ))} - - + {BadgeElement && ( + + + + )} + + + + ))} + + + {(pathname.startsWith('/manager/') || pathname === '/devportal/api-directory/your-products') && ( + + + + Gateway selected: + + + + + )} + + ); }; diff --git a/src/nextapp/pages/_app.tsx b/src/nextapp/pages/_app.tsx index 3e0a4e26b..2a9cd890b 100644 --- a/src/nextapp/pages/_app.tsx +++ b/src/nextapp/pages/_app.tsx @@ -69,6 +69,9 @@ const App: React.FC = ({ Component, pageProps }) => { return 'devportal'; }, [router]); + // Temp solution for handing spacing around new gateways dropdown menu + const gatewaysMenu = (router?.pathname.startsWith('/manager/') || router?.pathname === '/devportal/api-directory/your-products') + if (!queryClientRef.current) { queryClientRef.current = new QueryClient({ defaultOptions: { @@ -105,7 +108,7 @@ const App: React.FC = ({ Component, pageProps }) => { - + diff --git a/src/nextapp/pages/manager/namespaces/index.tsx b/src/nextapp/pages/manager/namespaces/index.tsx index 991053adf..ba8f48674 100644 --- a/src/nextapp/pages/manager/namespaces/index.tsx +++ b/src/nextapp/pages/manager/namespaces/index.tsx @@ -46,13 +46,14 @@ import { gql } from 'graphql-request'; import { restApi, useApiMutation } from '@/shared/services/api'; import { RiApps2Fill } from 'react-icons/ri'; import PreviewBanner from '@/components/preview-banner'; -import { useQueryClient } from 'react-query'; +import { QueryKey, useQueryClient } from 'react-query'; import { useRouter } from 'next/router'; import EmptyPane from '@/components/empty-pane'; -import NamespaceMenu from '@/components/namespace-menu/namespace-menu'; +import { Namespace, Query } from '@/shared/types/query.types'; import NewNamespace from '@/components/new-namespace'; import useCurrentNamespace from '@/shared/hooks/use-current-namespace'; import { useGlobal } from '@/shared/services/global'; +import EditNamespaceDisplayName from '@/components/edit-display-name'; const actions = [ { @@ -119,6 +120,7 @@ const NamespacesPage: React.FC = () => { const mutate = useApiMutation(mutation); const client = useQueryClient(); const namespace = useCurrentNamespace(); + const queryKey: QueryKey = ['currentNamespace']; const { isOpen, onClose, onOpen } = useDisclosure(); const global = useGlobal(); const currentOrg = React.useMemo(() => { @@ -166,69 +168,82 @@ const NamespacesPage: React.FC = () => { }, [client, mutate, router, toast, user]); const title = ( <> - - {user.namespace} - {namespace.data?.currentNamespace?.orgEnabled && ( - - - - - - )} - {(namespace.isFetching || namespace.isLoading) && ( )} {namespace.isSuccess && !namespace.isFetching && ( - - - - {currentOrg.text} - - {currentOrg.assigned && ( - - - - - - - + + {namespace.data?.currentNamespace?.displayName} + + {namespace.data?.currentNamespace?.orgEnabled && ( + - - - If you need to change the Organization or Business Unit for - your Namespace, submit a request through the{' '} - - Data Systems and Services request system - - - - - - )} - + + + + + )} + + + {namespace?.data.currentNamespace?.name} + + + + + {currentOrg.text} + + {currentOrg.assigned && ( + + + + + + + + + + If you need to change the Organization or Business Unit for + your Namespace, submit a request through the{' '} + + Data Systems and Services request system + + + + + + )} + + )} ); @@ -238,7 +253,9 @@ const NamespacesPage: React.FC = () => { API Program Services | Namespaces - {hasNamespace ? ` | ${user.namespace}` : ''} + {hasNamespace + ? ` | ${namespace.data?.currentNamespace?.displayName}` + : ''} @@ -253,11 +270,7 @@ const NamespacesPage: React.FC = () => { my={0} > - + CAN'T SELECT NAMESPACE HERE ANYMORE! or