diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml index ecec47d80..af57a5b5a 100644 --- a/.github/workflows/jira.yaml +++ b/.github/workflows/jira.yaml @@ -9,3 +9,18 @@ jobs: - uses: ikethecoder/sync-issues-github-jira@dev with: webhook-url: ${{ secrets.JIRA_WEBHOOK_URL }} + + cleanup-runs: + name: Delete workflow runs + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: Delete workflow runs + uses: Mattraks/delete-workflow-runs@v2 + with: + token: ${{ github.token }} + repository: ${{ github.repository }} + retain_days: 1 + keep_minimum_runs: 5 + delete_workflow_pattern: jira.yaml diff --git a/README.md b/README.md index 2c647ab6d..931b0f838 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # API Services Portal [![Lifecycle:Stable](https://img.shields.io/badge/Lifecycle-Stable-97ca00?style=for-the-badge)](https://github.com/bcgov/repomountie/blob/master/doc/lifecycle-badges.md) -[![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/bcgov/aps-portal/Build%20and%20Deploy/dev?style=for-the-badge)](https://github.com/bcgov/api-services-portal/actions/workflows/ci-build-deploy.yaml) +[![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/bcgov/api-services-portal/ci-build-deploy.yaml?branch=dev&style=for-the-badge)](https://github.com/bcgov/api-services-portal/actions/workflows/ci-build-deploy.yaml) [![Coverage](https://img.shields.io/sonar/coverage/aps-portal/dev?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge)](https://sonarcloud.io/summary/new_code?id=aps-portal) ![GitHub](https://img.shields.io/github/license/bcgov/aps-portal?style=for-the-badge) ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/bcgov/aps-portal?label=release&style=for-the-badge) @@ -58,10 +58,13 @@ Once running, the `api services portal` application is reachable via `localhost: 1. Create a `.env` from `.env.local` file 2. Create a `.env` from `.env.local` file under `feeds` directory -3. Run `docker-compose up` to spin up a local development environment with services (Postgres, Keycloak, OAuth2-proxy, APS-Portal, Feeder and Kong Gateway) -4. Go to: http://oauth2proxy.localtest.me:4180 -5. To login, use username `local` and password `local`, or username `awsummer@idir` and password `awsummer` -6. `docker-compose down` : Removes all the hosted services +3. Remove cypress from docker-compose file (L106-129 & L217-229) +4. Run build steps [here](https://github.com/bcgov/api-services-portal/tree/dev/e2e#build-gateway-api-image) +5. Run `docker-compose build` +5. Run `docker-compose up` to spin up a local development environment with services (Postgres, Keycloak, OAuth2-proxy, APS-Portal, Feeder and Kong Gateway) +6. Go to: http://oauth2proxy.localtest.me:4180 +7. To login, use username `local` and password `local`, or username `janis@idir` and password `awsummer` +8. `docker-compose down` : Removes all the hosted services ##### Note: diff --git a/e2e/cypress/fixtures/common-testdata.json b/e2e/cypress/fixtures/common-testdata.json new file mode 100644 index 000000000..fbdf793b0 --- /dev/null +++ b/e2e/cypress/fixtures/common-testdata.json @@ -0,0 +1,82 @@ +{ + "namespace": "newplatform", + "deleteResources": { + "namespace": "gw-a9232" + }, + "clientCredentials": { + "namespace": "gw-d2c50" + }, + "namespacePreview": { + "namespace": "gw-16a3a" + }, + "orgAssignment": { + "namespace": "gw-f016e" + }, + "orgAssignmentMultipleAdmin": { + "namespace": "orgassignment1" + }, + "orgAssignmentOrgUnit": { + "namespace": "orgassign-unit" + }, + "checkPermission": { + "namespace": "gw-deedd", + "grantPermission": { + "Mark": { + "userName": "Mark F Mark L", + "email": "mark@gmail.com", + "accessRole": [ + "Access.Manage" + ] + }, + "Mark_NV": { + "userName": "Mark F Mark L", + "email": "mark@gmail.com", + "accessRole": [ + "Namespace.View" + ] + }, + "Wendy": { + "userName": "Wendy F Wendy L", + "email": "wendy@test.com", + "accessRole": [ + "Namespace.Manage", + "CredentialIssuer.Admin" + ] + }, + "Wendy_NM": { + "userName": "Wendy F Wendy L", + "email": "wendy@test.com", + "accessRole": [ + "Namespace.Manage" + ] + }, + "Wendy_CA": { + "userName": "Wendy F Wendy L", + "email": "wendy@test.com", + "accessRole": [ + "CredentialIssuer.Admin" + ] + }, + "Wendy_GC": { + "userName": "Wendy F Wendy L", + "email": "wendy@test.com", + "accessRole": [ + "GatewayConfig.Publish", + "Namespace.View" + ] + }, + "Janis": { + "userName": "Janis Smith", + "email": "janis@testmail.com", + "accessRole": [ + "Namespace.Manage", + "CredentialIssuer.Admin" + ] + } + } + }, + "apiTest": { + "namespace": "gw-c3f31", + "delete_namespace": "testplatform" + } +} \ No newline at end of file diff --git a/e2e/cypress/fixtures/developer.json b/e2e/cypress/fixtures/developer.json index ce6b46712..2d6c22fe7 100644 --- a/e2e/cypress/fixtures/developer.json +++ b/e2e/cypress/fixtures/developer.json @@ -146,6 +146,16 @@ "environment": "dev" } }, + "deleteResources":{ + "application": { + "name": "Delete-Auto Test App", + "description": "Test application for auto test" + }, + "product": { + "name": "Delete-Auto Test Product", + "environment": "dev" + } + }, "elevatedAccess":{ "application": { "name": "Request for Elevated Acess", diff --git a/e2e/cypress/fixtures/service-plugin_A.yml b/e2e/cypress/fixtures/service-plugin_A.yml new file mode 100644 index 000000000..4e56a2c42 --- /dev/null +++ b/e2e/cypress/fixtures/service-plugin_A.yml @@ -0,0 +1,34 @@ +services: +- name: a-service-for-newplatform + host: httpbin.org + tags: [ns.newplatform.test] + port: 443 + protocol: https + retries: 0 + routes: + - name: a-service-for-newplatform-route + tags: [ns.newplatform.test] + hosts: + - a-service-for-newplatform.api.gov.bc.ca + paths: + - / + methods: + - GET + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + + plugins: + - name: key-auth + tags: [ ns.newplatform.test ] + protocols: [ http, https ] + config: + key_names: ["X-API-KEY"] + run_on_preflight: true + hide_credentials: true + key_in_body: false + - name: acl + tags: [ ns.newplatform.test ] + config: + hide_groups_header: true + allow: [ "406CB7CF" ] \ No newline at end of file diff --git a/e2e/cypress/fixtures/service-plugin_B.yml b/e2e/cypress/fixtures/service-plugin_B.yml new file mode 100644 index 000000000..0137af191 --- /dev/null +++ b/e2e/cypress/fixtures/service-plugin_B.yml @@ -0,0 +1,34 @@ +services: +- name: a-service-for-newplatform-test + host: httpbin.org + tags: [ns.newplatform.test] + port: 443 + protocol: https + retries: 0 + routes: + - name: a-service-for-newplatform-test-route + tags: [ns.newplatform.test] + hosts: + - a-service-for-newplatform-test.api.gov.bc.ca + paths: + - / + methods: + - GET + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + + plugins: + - name: key-auth + tags: [ ns.newplatform.test ] + protocols: [ http, https ] + config: + key_names: ["X-API-KEY"] + run_on_preflight: true + hide_credentials: true + key_in_body: false + - name: acl + tags: [ ns.newplatform.test ] + config: + hide_groups_header: true + allow: [ "9AA97172" ] diff --git a/e2e/cypress/pageObjects/authProfile.ts b/e2e/cypress/pageObjects/authProfile.ts index cb69ccb7a..b1e299642 100644 --- a/e2e/cypress/pageObjects/authProfile.ts +++ b/e2e/cypress/pageObjects/authProfile.ts @@ -175,7 +175,7 @@ class AuthorizationProfile { cy.wrap($e1).find('button').eq(1).click() cy.wait(2000) cy.wrap($e1).find('button').last().click({force: true}) - cy.verifyToastMessage(authProfileName +' deleted') + // cy.verifyToastMessage(authProfileName +' deleted') return false } }) diff --git a/e2e/cypress/pageObjects/consumers.ts b/e2e/cypress/pageObjects/consumers.ts index 93887f5b6..970554d92 100644 --- a/e2e/cypress/pageObjects/consumers.ts +++ b/e2e/cypress/pageObjects/consumers.ts @@ -45,6 +45,7 @@ export default class ConsumersPage { linkConsumerToNamespaceBtn: string = '[data-testid="link-consumer-namespace"]' userNameTxt: string = '[data-testid="link-consumer-username"]' linkBtn: string = '[data-testid="link-consumer-link-btn"]' + productDetails: string = '[data-testid="product-list-table"]' clickOnRateLimitingOption() { cy.get(this.rateLimitingOption, { timeout: 2000 }).click() @@ -202,7 +203,7 @@ export default class ConsumersPage { } editConsumerDialog() { - cy.get('[data-testid="product-list-table"]').then($button => { + cy.get(this.productDetails).then($button => { if ($button.is(':visible')) { cy.contains('Edit').first().click() } @@ -374,4 +375,12 @@ export default class ConsumersPage { getText() { cy.get('[data-testid="all-consumer-control-tbl"]').find('tr').last().find('td').first().find('a').as('inputValue') } + + revokeProductEnvAccess(prodEnv: any) { + cy.get(this.productDetails).find('tr').each(($row) => { + if ($row.find('td:nth-child(1)').text() == prodEnv) { + cy.wrap($row).find('button').last().click({ force: true }) + } + }) + } } \ No newline at end of file diff --git a/e2e/cypress/pageObjects/home.ts b/e2e/cypress/pageObjects/home.ts index 583e55f66..559257c20 100644 --- a/e2e/cypress/pageObjects/home.ts +++ b/e2e/cypress/pageObjects/home.ts @@ -56,7 +56,7 @@ class HomePage { cy.wait(1000) cy.get(this.namespaceNameInput).next('div').then(($ele) => { let validationMessage = $ele.text() - assert.equal(validationMessage,"Namespace name must be between 5 and 15 alpha-numeric lowercase characters and begin with an alphabet.") + assert.equal(validationMessage,"Namespace name must be between 5 and 15 alpha-numeric lowercase characters and start and end with an alphabet.") }) // cy.verifyToastMessage("Namespace create failed") }) diff --git a/e2e/cypress/support/auth-commands.ts b/e2e/cypress/support/auth-commands.ts index a031aed1c..ee920d741 100644 --- a/e2e/cypress/support/auth-commands.ts +++ b/e2e/cypress/support/auth-commands.ts @@ -95,17 +95,20 @@ Cypress.Commands.add('resetCredential', (accessRole: string) => { cy.visit('/') cy.reload() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.preserveCookies() cy.visit(login.path) - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(checkPermission.namespace) - cy.visit(na.path) - na.revokeAllPermission(checkPermission.grantPermission[accessRole].userName) - cy.wait(2000) - na.clickGrantUserAccessButton() - na.grantPermission(checkPermission.grantPermission[accessRole]) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(checkPermission.namespace) + cy.visit(na.path) + na.revokeAllPermission(checkPermission.grantPermission[accessRole].userName) + cy.wait(2000) + na.clickGrantUserAccessButton() + na.grantPermission(checkPermission.grantPermission[accessRole]) + }) }) }) @@ -241,7 +244,7 @@ Cypress.Commands.add('getServiceOrRouteID', (configType: string, host: string) = }).then((res) => { expect(res.status).to.eq(200) if (config === 'routes') { - cy.saveState(config + 'ID', Cypress._.get((Cypress._.filter(res.body.data, ["hosts", [host+".api.gov.bc.ca"]]))[0], 'id')) + cy.saveState(config + 'ID', Cypress._.get((Cypress._.filter(res.body.data, ["hosts", [host + ".api.gov.bc.ca"]]))[0], 'id')) } else { cy.saveState(config + 'ID', Cypress._.get((Cypress._.filter(res.body.data, ["name", host]))[0], 'id')) @@ -249,9 +252,18 @@ Cypress.Commands.add('getServiceOrRouteID', (configType: string, host: string) = }) }) -Cypress.Commands.add('publishApi', (fileName: string, namespace: string, flag?: boolean) => { +Cypress.Commands.add('publishApi', (fileNames: any, namespace: string, flag?: boolean) => { let fixtureFile = flag ? "state/regen" : "state/store"; cy.log('< Publish API') + let fileName = '' + if (fileNames instanceof Array) { + for (const filepath of fileNames) { + fileName = fileName + ' ./cypress/fixtures/' + filepath; + } + } + else { + fileName = ' ./cypress/fixtures/' + fileNames + } const requestName: string = 'publishAPI' cy.fixture(fixtureFile).then((creds: any) => { const serviceAcctCreds = JSON.parse(creds.credentials) @@ -263,7 +275,7 @@ Cypress.Commands.add('publishApi', (fileName: string, namespace: string, flag?: cy.executeCliCommand('gwa config set --token ' + res.body.access_token).then((response) => { { expect(response.stdout).to.contain("Config settings saved") - cy.executeCliCommand('gwa pg ./cypress/fixtures/' + fileName).then((response) => { + cy.executeCliCommand('gwa pg ' + fileName).then((response) => { expect(response.stdout).to.contain("Gateway config published") }) } @@ -502,7 +514,7 @@ Cypress.Commands.add('updatePropertiesOfPluginFile', (filename: string, property if (propertyName === "config.anonymous") { obj.plugins[0].config.anonymous = propertyValue } - else if (propertyName === "tags"){ + else if (propertyName === "tags") { obj.plugins[0][propertyName] = propertyValue } else { @@ -573,7 +585,7 @@ Cypress.Commands.add('forceVisit', (url: string) => { }); }); -Cypress.Commands.add('updateJsonBoby', (json: any, key: string, newValue: string):any => { +Cypress.Commands.add('updateJsonBoby', (json: any, key: string, newValue: string): any => { json[key] = newValue return json }); diff --git a/e2e/cypress/support/global.d.ts b/e2e/cypress/support/global.d.ts index 71d1f2e93..81337c99e 100644 --- a/e2e/cypress/support/global.d.ts +++ b/e2e/cypress/support/global.d.ts @@ -36,7 +36,7 @@ declare namespace Cypress { client_secret: string ): Chainable> - publishApi(fileName: string, namespace: string, flag?: boolean): Chainable> + publishApi(fileNames: any, namespace: string, flag?: boolean): Chainable> getServiceOrRouteID(configType: string, host: string ): Chainable> diff --git a/e2e/cypress/support/util-commands.ts b/e2e/cypress/support/util-commands.ts index 9d1653869..b18437f2d 100644 --- a/e2e/cypress/support/util-commands.ts +++ b/e2e/cypress/support/util-commands.ts @@ -111,9 +111,7 @@ Cypress.Commands.add('resetState', () => { }) Cypress.Commands.add('updateJsonValue', (filePath: string, jsonPath: string, newValue: string, index?: any) => { - debugger cy.readFile('cypress/fixtures/' + filePath).then(currState => { - debugger const keys = jsonPath.split('.'); // Split the keyPath using dot notation let currentObj = currState; @@ -142,7 +140,6 @@ Cypress.Commands.add('executeCliCommand', (command: string) => { }) Cypress.Commands.add('replaceWordInJsonObject', (targetWord: string, replacement: string, fileName: string) => { - debugger cy.readFile('cypress/fixtures/' + fileName).then((content: any) => { let regex = new RegExp(targetWord, 'g'); let modifiedString = content.replace(regex, replacement); @@ -165,7 +162,6 @@ Cypress.Commands.add('gwaPublish', (type: string, fileName: string) => { Cypress.Commands.add('deleteFileInE2EFolder', (fileName: string) => { const currentDirectory = Cypress.config('fileServerFolder'); // Get the current working directory const filePath = path.join(currentDirectory, fileName) - debugger try { cy.exec(`rm -f ${filePath}`); cy.log(`File '${fileName}' has been deleted from the e2e folder.`); 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 20f5bd96f..9fe06b03c 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 @@ -23,11 +23,12 @@ describe('Create API Spec', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.getUserSessionTokenValue(namespace, false).then((value) => { userSession = value }) @@ -47,19 +48,22 @@ describe('Create API Spec', () => { }); }) - it('creates new namespace', () => { + it('creates and activates new namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { nameSpace = namespace - cy.executeCliCommand('gwa namespace create -n ' + namespace).then((response) => { - assert.equal(response.stdout, namespace) + home.createNamespace(namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] }) }) }) }) - it('activates new namespace', () => { - home.useNamespace(nameSpace) + it('Verify for invalid namespace name', () => { + cy.get('@apiowner').then(({ invalid_namespace }: any) => { + home.validateNamespaceName(invalid_namespace) + }) }) it('creates a new service account', () => { @@ -71,16 +75,24 @@ describe('Create API Spec', () => { sa.saveServiceAcctCreds() }) +it('Verify gwa gateway publish multiple config file', () => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.publishApi(['service-plugin_A.yml','service-plugin_B.yml'], namespace).then((response: any) => { + expect(response.stdout).to.contain('Gateway config published'); + }) + }) +}) + it('publishes a new API for Dev environment to Kong Gateway', () => { - cy.get('@apiowner').then(({ namespace }: any) => { - cy.publishApi('service.yml', namespace).then((response: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.publishApi(['service.yml'], namespace).then((response: any) => { expect(response.stdout).to.contain('Sync successful'); }) }) }) it('Upload dataset and Product using GWA Apply command', () => { - cy.executeCliCommand('gwa apply').then((response) => { + cy.executeCliCommand('gwa apply -i gw-config.yml').then((response) => { let wordOccurrences = (response.stdout.match(/\bcreated\b/g) || []).length; expect(wordOccurrences).to.equal(2) }) @@ -103,7 +115,7 @@ describe('Create API Spec', () => { }) }) - it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { + it('update the Dataset in BC Data Catalogue to appear the API in the Directory', () => { cy.visit(pd.path) cy.get('@apiowner').then(({ product }: any) => { pd.updateDatasetNameToCatelogue(product.name, product.environment.name) @@ -131,7 +143,7 @@ describe('Create API Spec', () => { }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.publishApi('service-plugin.yml', namespace).then((response: any) => { expect(response.stdout).to.contain('Sync successful'); }) diff --git a/e2e/cypress/tests/01-api-key/02-team-access.cy.ts b/e2e/cypress/tests/01-api-key/02-team-access.cy.ts index 84161c65c..40c96c291 100644 --- a/e2e/cypress/tests/01-api-key/02-team-access.cy.ts +++ b/e2e/cypress/tests/01-api-key/02-team-access.cy.ts @@ -16,14 +16,17 @@ describe('Team Access Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) diff --git a/e2e/cypress/tests/01-api-key/03-request-access-inactive-env.cy.ts b/e2e/cypress/tests/01-api-key/03-request-access-inactive-env.cy.ts index cf01b89e4..20479b2a8 100644 --- a/e2e/cypress/tests/01-api-key/03-request-access-inactive-env.cy.ts +++ b/e2e/cypress/tests/01-api-key/03-request-access-inactive-env.cy.ts @@ -21,14 +21,17 @@ describe('Change an Active environment to Inactive', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) @@ -113,14 +116,17 @@ describe('Change an the environment back to active', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) diff --git a/e2e/cypress/tests/01-api-key/05-review-request-without-collecting-credentials.cy.ts b/e2e/cypress/tests/01-api-key/05-review-request-without-collecting-credentials.cy.ts index 39f89c4c7..ec1ac793b 100644 --- a/e2e/cypress/tests/01-api-key/05-review-request-without-collecting-credentials.cy.ts +++ b/e2e/cypress/tests/01-api-key/05-review-request-without-collecting-credentials.cy.ts @@ -17,13 +17,14 @@ describe('Approve Pending Request without collecting credentials Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('access-manager').as('access-manager') + cy.fixture('common-testdata').as('common-testdata') cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') }) it('authenticates Mark (Access-Manager)', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@access-manager').then(({ user }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); diff --git a/e2e/cypress/tests/01-api-key/07-approve-pending-rqst.cy.ts b/e2e/cypress/tests/01-api-key/07-approve-pending-rqst.cy.ts index 1dc6ca499..d48bc2e33 100644 --- a/e2e/cypress/tests/01-api-key/07-approve-pending-rqst.cy.ts +++ b/e2e/cypress/tests/01-api-key/07-approve-pending-rqst.cy.ts @@ -20,6 +20,7 @@ describe('Approve Pending Request Spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) @@ -33,7 +34,7 @@ describe('Approve Pending Request Spec', () => { }) it('authenticates Mark (Access-Manager)', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@access-manager').then(({ user }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); diff --git a/e2e/cypress/tests/01-api-key/08-grant-access.cy.ts b/e2e/cypress/tests/01-api-key/08-grant-access.cy.ts index 7f0ad2b15..238af646d 100644 --- a/e2e/cypress/tests/01-api-key/08-grant-access.cy.ts +++ b/e2e/cypress/tests/01-api-key/08-grant-access.cy.ts @@ -20,10 +20,11 @@ describe('Grant Access Spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Mark (Access-Manager)', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.get('@access-manager').then(({ user }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); 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 f39e1f99a..9e70f2a75 100644 --- a/e2e/cypress/tests/01-api-key/09-gwa-get.ts +++ b/e2e/cypress/tests/01-api-key/09-gwa-get.ts @@ -22,10 +22,11 @@ describe('Verify GWA get commands', () => { cy.preserveCookies() cy.fixture('api').as('api') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.getUserSessionTokenValue(namespace, false).then((value) => { userSession = value _namespace = namespace diff --git a/e2e/cypress/tests/01-api-key/10-revoke-access.cy.ts b/e2e/cypress/tests/01-api-key/10-revoke-access.cy.ts new file mode 100644 index 000000000..1a3751718 --- /dev/null +++ b/e2e/cypress/tests/01-api-key/10-revoke-access.cy.ts @@ -0,0 +1,92 @@ +import ConsumersPage from '../../pageObjects/consumers' +import LoginPage from '../../pageObjects/login' +import HomePage from '../../pageObjects/home' +import ProductPage from '../../pageObjects/products' + +describe('Revoke product environment access for Kong API spec', () => { + const login = new LoginPage() + const consumers = new ConsumersPage() + const home = new HomePage() + + before(() => { + cy.visit('/') + cy.reload() + cy.deleteAllCookies() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('access-manager').as('access-manager') + cy.fixture('apiowner').as('apiowner') + cy.fixture('developer').as('developer') + cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') + }) + + it('authenticates Mark (Access-Manager)', () => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.get('@access-manager').then(({ user }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(namespace); + }) + }) + }) + + it('Navigate to Consumer page and filter the product', () => { + cy.get('@apiowner').then(({ product }: any) => { + cy.visit(consumers.path); + consumers.filterConsumerByTypeAndValue('Products', product.name) + }) + }) + + it('Click on the first consumer', () => { + consumers.clickOnTheFirstConsumerID() + }) + + it('Revoke access for Test environment', () => { + cy.wait(1000) + consumers.revokeProductEnvAccess('Test') + }) + + it('Verify the confirmation message once the access is revoked', () => { + cy.verifyToastMessage("Product Revoked") + }) + + it('Navigate to Consumer page and filter the product', () => { + cy.get('@apiowner').then(({ product }: any) => { + cy.visit(consumers.path); + consumers.filterConsumerByTypeAndValue('Products', product.name) + }) + }) + + it('Click on the first consumer', () => { + consumers.clickOnTheFirstConsumerID() + }) + + it('Click on Grant Access button', () => { + cy.wait(1000) + consumers.clickOnGrantAccessBtn() + }) + + it('Grant Access to Test environment', () => { + cy.get('@apiowner').then(({ product }: any) => { + consumers.grantAccessToGivenProductEnvironment(product.name, product.test_environment.name) + }) + }) + + it('Verify that API is accessible with the generated API Key for Test environment', () => { + cy.get('@apiowner').then(({ product }: any) => { + cy.makeKongRequest(product.test_environment.config.serviceName, 'GET').then((response) => { + cy.log(response) + expect(response.status).to.be.equal(200) + }) + }) + }) + + after(() => { + cy.logout() + cy.clearLocalStorage({ log: true }) + cy.deleteAllCookies() + }) + +}) \ No newline at end of file diff --git a/e2e/cypress/tests/02-client-credential-flow/01-client-cred-team-access.cy.ts b/e2e/cypress/tests/02-client-credential-flow/01-client-cred-team-access.cy.ts index 24e2c4b36..c0ba4af3a 100644 --- a/e2e/cypress/tests/02-client-credential-flow/01-client-cred-team-access.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/01-client-cred-team-access.cy.ts @@ -19,6 +19,7 @@ describe('Grant appropriate permissions to team members for client credential fl beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -42,7 +43,7 @@ describe('Grant appropriate permissions to team members for client credential fl assert.isNotNaN(response.stdout) namespace = response.stdout cy.replaceWordInJsonObject('ccplatform', namespace, 'cc-service-gwa.yml') - cy.updateJsonValue('apiowner.json', 'clientCredentials.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'clientCredentials.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); diff --git a/e2e/cypress/tests/02-client-credential-flow/02-create_authorizarion_profile.cy.ts b/e2e/cypress/tests/02-client-credential-flow/02-create_authorizarion_profile.cy.ts index 8ad7591d6..e7b0ec4db 100644 --- a/e2e/cypress/tests/02-client-credential-flow/02-create_authorizarion_profile.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/02-create_authorizarion_profile.cy.ts @@ -19,6 +19,7 @@ describe('Generate Authorization Profiles', () => { cy.preserveCookies() cy.fixture('credential-issuer').as('credential-issuer') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Wendy (Credential-Issuer)', () => { @@ -30,7 +31,7 @@ describe('Generate Authorization Profiles', () => { }) it('Select the namespace created for client credential ', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { home.useNamespace(clientCredentials.namespace) }) }) 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 7766994a2..bfec3de48 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 @@ -26,11 +26,12 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -39,7 +40,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t it('Activates namespace for client credential flow tests', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { nameSpace = clientCredentials.namespace home.useNamespace(clientCredentials.namespace) cy.get('@login').then(function (xhr: any) { @@ -58,7 +59,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t }) it('Publishes a new API to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.publishApi('cc-service-gwa.yml', clientCredentials.namespace, true).then(() => { // cy.get('@publishAPIResponse').then((res: any) => { // // cy.log(JSON.stringify(res.body)) @@ -111,7 +112,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.replaceWordInJsonObject('ccplatform', clientCredentials.namespace, 'cc-service-plugin.yml') cy.wait(2000) cy.publishApi('cc-service-plugin.yml', clientCredentials.namespace,true).then(() => { @@ -153,7 +154,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t }) it('Applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.replaceWordInJsonObject('ccplatform', clientCredentials.namespace, 'cc-service-plugin.yml') cy.wait(2000) cy.publishApi('cc-service-plugin.yml', clientCredentials.namespace, true).then(() => { diff --git a/e2e/cypress/tests/02-client-credential-flow/05-cids-access-approve-api-rqst.cy.ts b/e2e/cypress/tests/02-client-credential-flow/05-cids-access-approve-api-rqst.cy.ts index 87693de85..37c0be416 100644 --- a/e2e/cypress/tests/02-client-credential-flow/05-cids-access-approve-api-rqst.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/05-cids-access-approve-api-rqst.cy.ts @@ -21,12 +21,13 @@ describe('Access manager approves developer access request for Client ID/Secret cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) @@ -152,12 +153,13 @@ describe('Deselect the scope from authorization tab', () => { cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') cy.fixture('manage-control-config-setting').as('manage-control-config-setting') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password).then(() => { home.useNamespace(clientCredentials.namespace); }) @@ -235,4 +237,62 @@ describe('Verify the selected client scoped is not displayed in assigned default cy.deleteAllCookies() }) -}) \ No newline at end of file +}) + +// describe('Revoke product environment access for Client Credential authorization spec', () => { +// const login = new LoginPage() +// const consumers = new ConsumersPage() +// const home = new HomePage() + +// before(() => { +// cy.visit('/') +// cy.deleteAllCookies() +// cy.reload() +// }) + +// beforeEach(() => { +// cy.preserveCookies() +// cy.fixture('access-manager').as('access-manager') +// cy.fixture('apiowner').as('apiowner') +// cy.fixture('developer').as('developer') +// cy.fixture('state/store').as('store') +// }) + +// it('authenticates Mark (Access-Manager)', () => { +// cy.get('@apiowner').then(({ clientCredentials }: any) => { +// cy.get('@access-manager').then(({ user }: any) => { +// cy.login(user.credentials.username, user.credentials.password) +// home.useNamespace(clientCredentials.namespace); +// }) +// }) +// }) + +// it('Navigate to Consumer page and filter the product', () => { +// cy.get('@apiowner').then(({ clientCredentials }: any) => { +// cy.visit(consumers.path); +// let product = clientCredentials.clientIdSecret.product +// consumers.filterConsumerByTypeAndValue('Products', product.name) +// }) +// }) + +// it('Click on the first consumer', () => { +// consumers.clickOnTheFirstConsumerID() +// }) + +// it('Revoke access for Test environment', () => { +// cy.wait(1000) +// consumers.revokeProductEnvAccess('Test') +// }) + +// it('Verify the confirmation message once the access is revoked', () => { +// cy.verifyToastMessage("Product Revoked") +// }) + + +// after(() => { +// cy.logout() +// cy.clearLocalStorage({ log: true }) +// cy.deleteAllCookies() +// }) + +// }) \ No newline at end of file diff --git a/e2e/cypress/tests/02-client-credential-flow/07-jwt-genkp-access-approve-api-rqst.cy.ts b/e2e/cypress/tests/02-client-credential-flow/07-jwt-genkp-access-approve-api-rqst.cy.ts index 6ee1c408a..140eb982e 100644 --- a/e2e/cypress/tests/02-client-credential-flow/07-jwt-genkp-access-approve-api-rqst.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/07-jwt-genkp-access-approve-api-rqst.cy.ts @@ -19,12 +19,13 @@ describe('Access manager approves developer access request for JWT - Generated K cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) diff --git a/e2e/cypress/tests/02-client-credential-flow/09-jwks-url-access-approval-api-rqst.cy.ts b/e2e/cypress/tests/02-client-credential-flow/09-jwks-url-access-approval-api-rqst.cy.ts index e44f75b99..a46bf0002 100644 --- a/e2e/cypress/tests/02-client-credential-flow/09-jwks-url-access-approval-api-rqst.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/09-jwks-url-access-approval-api-rqst.cy.ts @@ -20,12 +20,13 @@ describe('Access manager approves developer access request for JWKS URL flow', ( cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) diff --git a/e2e/cypress/tests/02-client-credential-flow/11-jwt-publlicKey-access-approve-api-rqst.cy.ts b/e2e/cypress/tests/02-client-credential-flow/11-jwt-publlicKey-access-approve-api-rqst.cy.ts index a0818d6f5..aa58e9d32 100644 --- a/e2e/cypress/tests/02-client-credential-flow/11-jwt-publlicKey-access-approve-api-rqst.cy.ts +++ b/e2e/cypress/tests/02-client-credential-flow/11-jwt-publlicKey-access-approve-api-rqst.cy.ts @@ -19,12 +19,13 @@ describe('Access manager approves developer access request for JWT - Generated K cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) diff --git a/e2e/cypress/tests/03-manage-labels/02-approve-pending-rqst-for-labels.spec.cy.ts b/e2e/cypress/tests/03-manage-labels/02-approve-pending-rqst-for-labels.spec.cy.ts index acd3830b3..8acba76ce 100644 --- a/e2e/cypress/tests/03-manage-labels/02-approve-pending-rqst-for-labels.spec.cy.ts +++ b/e2e/cypress/tests/03-manage-labels/02-approve-pending-rqst-for-labels.spec.cy.ts @@ -20,12 +20,13 @@ describe('Approve Pending Request Spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); }) diff --git a/e2e/cypress/tests/03-manage-labels/03-filter-labels.cy.ts b/e2e/cypress/tests/03-manage-labels/03-filter-labels.cy.ts index d79910e41..7e4f00872 100644 --- a/e2e/cypress/tests/03-manage-labels/03-filter-labels.cy.ts +++ b/e2e/cypress/tests/03-manage-labels/03-filter-labels.cy.ts @@ -20,13 +20,14 @@ describe('Filter Manage labels Spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); }) diff --git a/e2e/cypress/tests/03-manage-labels/04-manage-labels.cy.ts b/e2e/cypress/tests/03-manage-labels/04-manage-labels.cy.ts index e6bee8d2f..0fd11e4bc 100644 --- a/e2e/cypress/tests/03-manage-labels/04-manage-labels.cy.ts +++ b/e2e/cypress/tests/03-manage-labels/04-manage-labels.cy.ts @@ -20,11 +20,12 @@ describe('Manage/Edit labels spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); }) diff --git a/e2e/cypress/tests/03-manage-labels/05-link-consumers.ts b/e2e/cypress/tests/03-manage-labels/05-link-consumers.ts index 97eea0365..0d69e9158 100644 --- a/e2e/cypress/tests/03-manage-labels/05-link-consumers.ts +++ b/e2e/cypress/tests/03-manage-labels/05-link-consumers.ts @@ -21,12 +21,13 @@ describe('Link Consumers to Namespace', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); }) diff --git a/e2e/cypress/tests/04-gateway-services/01-gateway-service-details.cy.ts b/e2e/cypress/tests/04-gateway-services/01-gateway-service-details.cy.ts index 37134dcb8..69c915900 100644 --- a/e2e/cypress/tests/04-gateway-services/01-gateway-service-details.cy.ts +++ b/e2e/cypress/tests/04-gateway-services/01-gateway-service-details.cy.ts @@ -19,13 +19,16 @@ describe('Verify Gateway Service details', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) @@ -49,7 +52,7 @@ describe('Verify Gateway Service details', () => { it('Verify the routes details ', () => { cy.get('@apiowner').then(({ product }: any) => { - gs.verifyRouteName(product.environment.config.serviceName, 'https://'+product.environment.config.serviceName+'.api.gov.bc.ca/') + gs.verifyRouteName(product.environment.config.serviceName, 'https://' + product.environment.config.serviceName + '.api.gov.bc.ca/') }) }) @@ -58,8 +61,8 @@ describe('Verify Gateway Service details', () => { }) it('Verify the Tags details ', () => { - cy.get('@apiowner').then(({ namespace }: any) => { - gs.verifyTagsName('ns.'+namespace) + cy.get('@common-testdata').then(({ namespace }: any) => { + gs.verifyTagsName('ns.' + namespace) }) }) diff --git a/e2e/cypress/tests/04-gateway-services/02-filter-gateway-service.cy.ts b/e2e/cypress/tests/04-gateway-services/02-filter-gateway-service.cy.ts index 578c0c581..df77485fd 100644 --- a/e2e/cypress/tests/04-gateway-services/02-filter-gateway-service.cy.ts +++ b/e2e/cypress/tests/04-gateway-services/02-filter-gateway-service.cy.ts @@ -21,13 +21,16 @@ describe('Filter Gateway Services Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@apiowner').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) diff --git a/e2e/cypress/tests/05-migrate-user/01-migrate-user-access.cy.ts b/e2e/cypress/tests/05-migrate-user/01-migrate-user-access.cy.ts index 029ee9fd8..bd817ab6e 100644 --- a/e2e/cypress/tests/05-migrate-user/01-migrate-user-access.cy.ts +++ b/e2e/cypress/tests/05-migrate-user/01-migrate-user-access.cy.ts @@ -17,14 +17,17 @@ describe('Assign Access to existing user Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) @@ -62,12 +65,13 @@ describe('Authernticate with old user to initiate migration', () => { cy.preserveCookies() cy.fixture('usermigration').as('usermigration') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates with old user', () => { cy.get('@usermigration').then(({ oldUser }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(oldUser.credentials.username, oldUser.credentials.password) cy.log('Logged in!') home.useNamespace(namespace) @@ -98,6 +102,7 @@ describe('Verify that permission of old user is migrated to new user', () => { cy.preserveCookies() cy.fixture('usermigration').as('usermigration') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -116,7 +121,7 @@ describe('Verify that permission of old user is migrated to new user', () => { it('Get the permission of the user', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { home.useNamespace(namespace) cy.get('@login').then(function (xhr: any) { userScopes = xhr.response.body.user.scopes @@ -154,6 +159,7 @@ describe('Verify that old user is no longer able to sign in', () => { cy.preserveCookies() cy.fixture('usermigration').as('usermigration') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) diff --git a/e2e/cypress/tests/07-manage-control/01-ip-restriction.cy.ts b/e2e/cypress/tests/07-manage-control/01-ip-restriction.cy.ts index aed31e1d2..2aeeb2f6e 100644 --- a/e2e/cypress/tests/07-manage-control/01-ip-restriction.cy.ts +++ b/e2e/cypress/tests/07-manage-control/01-ip-restriction.cy.ts @@ -18,11 +18,12 @@ describe('Manage Control-IP Restriction Spec', () => { cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') cy.fixture('manage-control-config-setting').as('manage-control-config-setting') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Mark (Access Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password).then(() => { home.useNamespace(namespace); }) @@ -150,12 +151,10 @@ describe('Manage Control -Apply IP Restriction to Global and Consumer at Service }) it('set IP address that is not accessible in the network as allowed IP and set service as scope', () => { - cy.get('@access-manager').then(({ user, namespace }: any) => { - cy.visit(consumers.path); - consumers.clickOnTheFirstConsumerID() - cy.get('@manage-control-config-setting').then(({ ipRestriction }: any) => { - consumers.setAllowedIPAddress(ipRestriction.ipRange_inValid) - }) + cy.visit(consumers.path); + consumers.clickOnTheFirstConsumerID() + cy.get('@manage-control-config-setting').then(({ ipRestriction }: any) => { + consumers.setAllowedIPAddress(ipRestriction.ipRange_inValid) }) }) diff --git a/e2e/cypress/tests/07-manage-control/02-rate-limiting.cy.ts b/e2e/cypress/tests/07-manage-control/02-rate-limiting.cy.ts index 7dd342b09..c73464263 100644 --- a/e2e/cypress/tests/07-manage-control/02-rate-limiting.cy.ts +++ b/e2e/cypress/tests/07-manage-control/02-rate-limiting.cy.ts @@ -20,12 +20,13 @@ describe('Manage Control-Rate Limiting Spec for Service as Scope and Local Polic cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') cy.fixture('manage-control-config-setting').as('manage-control-config-setting') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password).then(() => { home.useNamespace(namespace); }) diff --git a/e2e/cypress/tests/07-manage-control/03-kong-api-only-apply-rate-limiting.cy.ts b/e2e/cypress/tests/07-manage-control/03-kong-api-only-apply-rate-limiting.cy.ts index 4c0b63f0d..ad0d5c8f8 100644 --- a/e2e/cypress/tests/07-manage-control/03-kong-api-only-apply-rate-limiting.cy.ts +++ b/e2e/cypress/tests/07-manage-control/03-kong-api-only-apply-rate-limiting.cy.ts @@ -30,6 +30,7 @@ describe('Apply Kong API key only plugin', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates api owner', () => { @@ -40,7 +41,7 @@ describe('Apply Kong API key only plugin', () => { it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { nameSpace = namespace home.useNamespace(namespace) }) @@ -79,12 +80,14 @@ describe('Apply Kong API key only plugin', () => { }) it('Apply Key-auth only authorization plugin to Kong Gateway', () => { - cy.get('@apiowner').then(({ namespace, product }: any) => { - cy.updatePluginFile('service-plugin.yml', product.environment.config.serviceName, 'service-plugin-key-auth-only.yml') - cy.publishApi('service-plugin.yml', namespace).then(() => { - // cy.get('@publishAPIResponse').then((res: any) => { - // cy.log(JSON.stringify(res.body)) - // }) + cy.get('@apiowner').then(({ product }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.updatePluginFile('service-plugin.yml', product.environment.config.serviceName, 'service-plugin-key-auth-only.yml') + cy.publishApi('service-plugin.yml', namespace).then(() => { + // cy.get('@publishAPIResponse').then((res: any) => { + // cy.log(JSON.stringify(res.body)) + // }) + }) }) }) }) @@ -196,12 +199,13 @@ describe('Approve Pending Request Spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); }) diff --git a/e2e/cypress/tests/08-client-role/01-keycloak-set-roles.cy.ts b/e2e/cypress/tests/08-client-role/01-keycloak-set-roles.cy.ts index 3b84388af..55a09be3c 100644 --- a/e2e/cypress/tests/08-client-role/01-keycloak-set-roles.cy.ts +++ b/e2e/cypress/tests/08-client-role/01-keycloak-set-roles.cy.ts @@ -28,6 +28,7 @@ describe('Set roles in Keycloak auth client', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') cy.fixture('admin').as('admin') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Admin owner', () => { diff --git a/e2e/cypress/tests/08-client-role/02-add-roles-authorization-profile.ts b/e2e/cypress/tests/08-client-role/02-add-roles-authorization-profile.ts index b3094ced3..e6ed788f7 100644 --- a/e2e/cypress/tests/08-client-role/02-add-roles-authorization-profile.ts +++ b/e2e/cypress/tests/08-client-role/02-add-roles-authorization-profile.ts @@ -20,6 +20,7 @@ describe('Apply client roles to the Authorization Profile', () => { cy.preserveCookies() cy.fixture('credential-issuer').as('credential-issuer') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Wendy (Credential-Issuer)', () => { @@ -31,7 +32,7 @@ describe('Apply client roles to the Authorization Profile', () => { }) it('Select the namespace created for client credential ', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { home.useNamespace(clientCredentials.namespace) }) }) diff --git a/e2e/cypress/tests/08-client-role/03-read-client-role.ts b/e2e/cypress/tests/08-client-role/03-read-client-role.ts index 44d50fd85..b221da16f 100644 --- a/e2e/cypress/tests/08-client-role/03-read-client-role.ts +++ b/e2e/cypress/tests/08-client-role/03-read-client-role.ts @@ -20,6 +20,7 @@ describe('Developer creates an access request for Client ID/Secret authenticator beforeEach(() => { cy.preserveCookies() cy.fixture('developer').as('developer') + // cy.visit(login.path) }) @@ -75,12 +76,13 @@ describe('Access manager apply "Read" role and approves developer access request cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) @@ -123,6 +125,7 @@ describe('Make an API request using Client ID, Secret, and Access Token', () => describe('Update Kong plugin and verify that only only GET method is allowed for Read role', () => { beforeEach(() => { cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Set allowed method "GET" in kong plugin ', () => { @@ -142,7 +145,7 @@ describe('Update Kong plugin and verify that only only GET method is allowed for }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.publishApi('cc-service-plugin.yml', clientCredentials.namespace, true).then(() => { // cy.get('@publishAPIResponse').then((res: any) => { // cy.log(JSON.stringify(res.body)) diff --git a/e2e/cypress/tests/08-client-role/04-write-client-role.ts b/e2e/cypress/tests/08-client-role/04-write-client-role.ts index 060933ec4..f4250cd3f 100644 --- a/e2e/cypress/tests/08-client-role/04-write-client-role.ts +++ b/e2e/cypress/tests/08-client-role/04-write-client-role.ts @@ -75,12 +75,13 @@ describe('Access manager apply "Write" role and approves developer access reques cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) @@ -114,6 +115,7 @@ describe('Access manager apply "Write" role and approves developer access reques describe('Update Kong plugin and verify that only only PUT and POST methods are allowed for Read role', () => { beforeEach(() => { cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Set allowed methods "PUT" and "POST" in kong plugin ', () => { @@ -133,7 +135,7 @@ describe('Update Kong plugin and verify that only only PUT and POST methods are }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.publishApi('cc-service-plugin.yml', clientCredentials.namespace, true).then(() => { // cy.get('@publishAPIResponse').then((res: any) => { // cy.log(JSON.stringify(res.body)) diff --git a/e2e/cypress/tests/08-client-role/05-check-without-role.ts b/e2e/cypress/tests/08-client-role/05-check-without-role.ts index e159768ea..2f2c86b91 100644 --- a/e2e/cypress/tests/08-client-role/05-check-without-role.ts +++ b/e2e/cypress/tests/08-client-role/05-check-without-role.ts @@ -30,6 +30,7 @@ describe('Reset Authorization profile to default (without any role)', () => { cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -40,7 +41,7 @@ describe('Reset Authorization profile to default (without any role)', () => { }) it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { nameSpace = clientCredentials.namespace home.useNamespace(clientCredentials.namespace) }) @@ -57,7 +58,7 @@ describe('Reset Authorization profile to default (without any role)', () => { }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.replaceWordInJsonObject('ccplatform', nameSpace, 'cc-service-plugin.yml') cy.publishApi('cc-service-plugin.yml', clientCredentials.namespace,true).then(() => { // cy.get('@publishAPIResponse').then((res: any) => { @@ -90,6 +91,7 @@ describe('Check service access without applying any roles', () => { cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) diff --git a/e2e/cypress/tests/09-update-product-env/01-client-credential-to-kong-acl-api.cy.ts b/e2e/cypress/tests/09-update-product-env/01-client-credential-to-kong-acl-api.cy.ts index 3d42867b8..7b5a35813 100644 --- a/e2e/cypress/tests/09-update-product-env/01-client-credential-to-kong-acl-api.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/01-client-credential-to-kong-acl-api.cy.ts @@ -30,6 +30,7 @@ describe('Change Authorization profile', () => { cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -40,7 +41,7 @@ describe('Change Authorization profile', () => { }) it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { nameSpace = clientCredentials.namespace home.useNamespace(clientCredentials.namespace) cy.get('@login').then(function (xhr: any) { @@ -70,7 +71,7 @@ describe('Change Authorization profile', () => { }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.publishApi('cc-service-plugin.yml', clientCredentials.namespace,true).then(() => { // cy.get('@publishAPIResponse').then((res: any) => { // cy.log(JSON.stringify(res.body)) @@ -181,12 +182,13 @@ describe('Access manager approves developer access request for Kong API ACL auth cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(clientCredentials.namespace) }) diff --git a/e2e/cypress/tests/09-update-product-env/02-kong-acl-api-to-client-credential.cy.ts b/e2e/cypress/tests/09-update-product-env/02-kong-acl-api-to-client-credential.cy.ts index dd50eed3e..089bcf725 100644 --- a/e2e/cypress/tests/09-update-product-env/02-kong-acl-api-to-client-credential.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/02-kong-acl-api-to-client-credential.cy.ts @@ -33,6 +33,7 @@ describe('Change Authorization profile from Kong ACL-API to Client Credential', cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -43,7 +44,7 @@ describe('Change Authorization profile from Kong ACL-API to Client Credential', }) it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { nameSpace = namespace home.useNamespace(namespace) }) @@ -187,12 +188,13 @@ describe('Access manager approves developer access request for Client ID/Secret cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace) }) diff --git a/e2e/cypress/tests/09-update-product-env/03-apply-multiple-services.cy.ts b/e2e/cypress/tests/09-update-product-env/03-apply-multiple-services.cy.ts index 6de9001f0..3cc4580bf 100644 --- a/e2e/cypress/tests/09-update-product-env/03-apply-multiple-services.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/03-apply-multiple-services.cy.ts @@ -35,6 +35,7 @@ describe('Apply multiple services to the product environment', () => { cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -45,7 +46,7 @@ describe('Apply multiple services to the product environment', () => { }) it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { nameSpace = namespace home.useNamespace(namespace) }) @@ -69,7 +70,7 @@ describe('Apply multiple services to the product environment', () => { }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.publishApi('service-plugin.yml', namespace).then(() => { // cy.get('@publishAPIResponse').then((res: any) => { // cy.log(JSON.stringify(res.body)) @@ -214,12 +215,13 @@ describe('Access manager approves developer access request for Client ID/Secret cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('Access Manager logs in', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace) }) diff --git a/e2e/cypress/tests/09-update-product-env/04-change-env-status copy.cy.ts b/e2e/cypress/tests/09-update-product-env/04-change-env-status copy.cy.ts index 17b5b718e..0eb83a20d 100644 --- a/e2e/cypress/tests/09-update-product-env/04-change-env-status copy.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/04-change-env-status copy.cy.ts @@ -34,6 +34,7 @@ describe('Change Product environment from active to inactive', () => { cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -44,7 +45,7 @@ describe('Change Product environment from active to inactive', () => { }) it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { nameSpace = namespace home.useNamespace(namespace) }) diff --git a/e2e/cypress/tests/09-update-product-env/05-keycloak-shared-IDP-config.cy.ts b/e2e/cypress/tests/09-update-product-env/05-keycloak-shared-IDP-config.cy.ts index 7b38cefae..bcbfcf9c4 100644 --- a/e2e/cypress/tests/09-update-product-env/05-keycloak-shared-IDP-config.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/05-keycloak-shared-IDP-config.cy.ts @@ -28,6 +28,7 @@ describe('Apply Shared IDP config at Keycloak user group', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') cy.fixture('admin').as('admin') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Admin owner', () => { @@ -42,7 +43,7 @@ describe('Apply Shared IDP config at Keycloak user group', () => { }) it('Edit the namespace from the tree view', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.contains(namespace).click() userGroups.clickOnEditButton() }) 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 c56d84a7c..9e5c3d28e 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 @@ -28,16 +28,19 @@ describe('Apply Shared IDP while creating Authorization Profile', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - home.useNamespace(namespace) - cy.get('@login').then(function (xhr: any) { - userSession = xhr.response.headers['x-auth-request-access-token'] + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) }) }) }) @@ -52,8 +55,8 @@ describe('Apply Shared IDP while creating Authorization Profile', () => { }) it('Publish the Shared IDP profile', () => { - cy.get('@apiowner').then(({ namespace }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/'+namespace+'/issuers', 'PUT').then((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) expect(response.body.result).to.be.contain('created') }) @@ -94,16 +97,19 @@ describe('Update IDP issuer for shared IDP profile', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - home.useNamespace(namespace) - cy.get('@login').then(function (xhr: any) { - userSession = xhr.response.headers['x-auth-request-access-token'] + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) }) }) }) @@ -118,8 +124,8 @@ describe('Update IDP issuer for shared IDP profile', () => { }) it('Put the resource and verify the success code in the response', () => { - cy.get('@apiowner').then(({ namespace }: any) => { - cy.makeAPIRequest('ds/api/v2/namespaces/'+namespace+'/issuers', 'PUT').then((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) }) }) diff --git a/e2e/cypress/tests/09-update-product-env/07-kong-public-auth.ts b/e2e/cypress/tests/09-update-product-env/07-kong-public-auth.ts index 39115b439..1f448d46d 100644 --- a/e2e/cypress/tests/09-update-product-env/07-kong-public-auth.ts +++ b/e2e/cypress/tests/09-update-product-env/07-kong-public-auth.ts @@ -30,6 +30,7 @@ describe('Verify for Kong Public Auth', () => { cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) @@ -40,7 +41,7 @@ describe('Verify for Kong Public Auth', () => { }) it('Activates the namespace', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { nameSpace = clientCredentials.namespace home.useNamespace(clientCredentials.namespace) cy.get('@login').then(function (xhr: any) { @@ -69,7 +70,7 @@ describe('Verify for Kong Public Auth', () => { }) it('applies authorization plugin to service published to Kong Gateway', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { cy.publishApi('cc-service-gwa.yml', clientCredentials.namespace,true).then((response:any) => { expect(response.stdout).to.contain('Sync successful'); }) 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 9ffeb309f..715ea8324 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 @@ -22,11 +22,12 @@ describe('Create API Spec for Delete Resources', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -45,7 +46,7 @@ describe('Create API Spec for Delete Resources', () => { assert.isNotNaN(response.stdout) namespace = response.stdout cy.replaceWordInJsonObject('ns.deleteplatform', 'ns.' + namespace, 'service-clear-resources-gwa.yml') - cy.updateJsonValue('apiowner.json', 'deleteResources.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'deleteResources.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); @@ -64,7 +65,7 @@ describe('Create API Spec for Delete Resources', () => { }) it('publishes a new API to Kong Gateway', () => { - cy.get('@apiowner').then(({ deleteResources }: any) => { + cy.get('@common-testdata').then(({ deleteResources }: any) => { cy.publishApi('service-clear-resources-gwa.yml', namespace).then((response: any) => { expect(response.stdout).to.contain('Sync successful'); }) @@ -101,7 +102,7 @@ describe('Create API Spec for Delete Resources', () => { cy.visit(pd.path) cy.get('@apiowner').then(({ deleteResources }: any) => { pd.editProductEnvironment(deleteResources.product.name, deleteResources.product.environment.name) - pd.editProductEnvironmentConfig(deleteResources.product.environment.config) + pd.editProductEnvironmentConfig(deleteResources.product.environment.config, false, false) pd.generateKongPluginConfig(deleteResources.product.name, deleteResources.product.environment.name, 'service-clear-resources.yml') }) }) diff --git a/e2e/cypress/tests/10-clear-resources/02-team-access.cy.ts b/e2e/cypress/tests/10-clear-resources/02-team-access.cy.ts new file mode 100644 index 000000000..0b9d69f7c --- /dev/null +++ b/e2e/cypress/tests/10-clear-resources/02-team-access.cy.ts @@ -0,0 +1,54 @@ +import HomePage from '../../pageObjects/home' +import LoginPage from '../../pageObjects/login' +import NamespaceAccessPage from '../../pageObjects/namespaceAccess' + +describe('Team Access Spec', () => { + const login = new LoginPage() + const home = new HomePage() + const na = new NamespaceAccessPage() + + before(() => { + cy.visit('/') + cy.reload() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') + // cy.visit(login.path) + }) + + it('authenticates Janis (api owner)', () => { + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ deleteResources }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(deleteResources.namespace) + }) + }) + }) + + it('Navigate to Namespace Access Page', () => { + cy.visit(na.path) + cy.wait(2000) + }) + + it('Grant namespace access to Mark (access manager)', () => { + cy.get('@apiowner').then(({ grantPermission }: any) => { + na.clickGrantUserAccessButton() + na.grantPermission(grantPermission.Mark) + }) + }) + + it('Grant CredentialIssuer.Admin permission to Janis (API Owner)', () => { + cy.get('@apiowner').then(({ grantPermission }: any) => { + na.editPermission(grantPermission.Janis) + }) + }) + + after(() => { + cy.logout() + + }) +}) diff --git a/e2e/cypress/tests/10-clear-resources/03-rqst-access.cy.ts b/e2e/cypress/tests/10-clear-resources/03-rqst-access.cy.ts new file mode 100644 index 000000000..8fda458ad --- /dev/null +++ b/e2e/cypress/tests/10-clear-resources/03-rqst-access.cy.ts @@ -0,0 +1,51 @@ +import ApiDirectoryPage from '../../pageObjects/apiDirectory' +import ApplicationPage from '../../pageObjects/applications' +import LoginPage from '../../pageObjects/login' +import MyAccessPage from '../../pageObjects/myAccess' + +describe('Request Access Spec', () => { + const login = new LoginPage() + const apiDir = new ApiDirectoryPage() + const app = new ApplicationPage() + const myAccessPage = new MyAccessPage() + + before(() => { + cy.visit('/') + cy.reload() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('developer').as('developer') + cy.fixture('common-testdata').as('common-testdata') + cy.visit(login.path) + }) + + it('authenticates Harley (developer)', () => { + cy.get('@developer').then(({ user }: any) => { + cy.login(user.credentials.username, user.credentials.password) + }) + }) + + it('creates an application', () => { + cy.visit(app.path) + cy.get('@developer').then(({ deleteResources }: any) => { + app.createApplication(deleteResources.application) + }) + }) + + it('creates an access request', () => { + cy.visit(apiDir.path) + cy.get('@developer').then(({ deleteResources, accessRequest }: any) => { + apiDir.createAccessRequest(deleteResources.product, deleteResources.application, accessRequest) + myAccessPage.clickOnGenerateSecretButton() + cy.contains("API Key").should('be.visible') + myAccessPage.saveAPIKeyValue() + }) + }) + + after(() => { + cy.logout() + + }) +}) diff --git a/e2e/cypress/tests/10-clear-resources/04-delete-consumer.ts b/e2e/cypress/tests/10-clear-resources/04-delete-consumer.ts new file mode 100644 index 000000000..b29d95b28 --- /dev/null +++ b/e2e/cypress/tests/10-clear-resources/04-delete-consumer.ts @@ -0,0 +1,66 @@ +import ConsumersPage from '../../pageObjects/consumers' +import HomePage from '../../pageObjects/home' +import LoginPage from '../../pageObjects/login' +import NameSpacePage from '../../pageObjects/namespace' +import Products from '../../pageObjects/products' +import ServiceAccountsPage from '../../pageObjects/serviceAccounts' + +describe('Delete created consumer', () => { + const home = new HomePage() + const sa = new ServiceAccountsPage() + const pd = new Products() + const ns = new NameSpacePage + const consumers = new ConsumersPage() + let flag: boolean + let consumerID: any + + before(() => { + cy.visit('/') + cy.reload() + // cy.resetState() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('access-manager').as('access-manager') + cy.fixture('common-testdata').as('common-testdata') + cy.fixture('apiowner').as('apiowner') + }) + + it('authenticates Mark (access manager)', () => { + cy.get('@access-manager').then(({ user }: any) => { + cy.get('@common-testdata').then(({ deleteResources }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(deleteResources.namespace); + }) + }) + }) + + it('Navigates to Consumer page', () => { + cy.visit(consumers.path) + }) + + it('Get the consumer ID from the list', () => { + cy.getLastConsumerID().then((title) => { + consumerID = title + }) + }) + + it('Delete the consumer ID from the list', () => { + consumers.deleteConsumer(consumerID) + }) + + it('Verify the confirmation message to delete the consumer', () => { + cy.contains('This action cannot be undone').should('exist') + cy.contains('Yes, Delete').click() + }) + + it('Verify toast message for consumer deletion', () => { + cy.verifyToastMessage("Consumer deleted") + }) + + after(() => { + cy.logout() + + }) +}) diff --git a/e2e/cypress/tests/10-clear-resources/02-delete-resources.cy.ts b/e2e/cypress/tests/10-clear-resources/05-delete-resources.cy.ts similarity index 80% rename from e2e/cypress/tests/10-clear-resources/02-delete-resources.cy.ts rename to e2e/cypress/tests/10-clear-resources/05-delete-resources.cy.ts index a8ca538f4..6a58f6ed9 100644 --- a/e2e/cypress/tests/10-clear-resources/02-delete-resources.cy.ts +++ b/e2e/cypress/tests/10-clear-resources/05-delete-resources.cy.ts @@ -14,7 +14,6 @@ describe('Delete created resources', () => { before(() => { cy.visit('/') - cy.deleteAllCookies() cy.reload() // cy.resetState() }) @@ -22,13 +21,16 @@ describe('Delete created resources', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, deleteResources }: any) => { - cy.login(user.credentials.username, user.credentials.password) - home.useNamespace(deleteResources.namespace); + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ deleteResources }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(deleteResources.namespace); + }) }) }) @@ -56,7 +58,7 @@ describe('Delete created resources', () => { }) it('Delete Namespace', () => { - cy.get('@apiowner').then(({ deleteResources }: any) => { + cy.get('@common-testdata').then(({ deleteResources }: any) => { cy.visit(ns.path) ns.deleteNamespace(deleteResources.namespace) }) @@ -66,7 +68,7 @@ describe('Delete created resources', () => { cy.on('fail', (err, runnable) => { flag = false }) - cy.get('@apiowner').then(({ deleteResources }: any) => { + cy.get('@common-testdata').then(({ deleteResources }: any) => { flag = true home.useNamespace(deleteResources.namespace) }) @@ -78,7 +80,6 @@ describe('Delete created resources', () => { after(() => { cy.logout() - cy.clearLocalStorage({ log: true }) - cy.deleteAllCookies() + }) }) diff --git a/e2e/cypress/tests/10-clear-resources/03-delete-service-acc.ts b/e2e/cypress/tests/10-clear-resources/06-delete-service-acc.ts similarity index 81% rename from e2e/cypress/tests/10-clear-resources/03-delete-service-acc.ts rename to e2e/cypress/tests/10-clear-resources/06-delete-service-acc.ts index 205cbc26e..2a353f424 100644 --- a/e2e/cypress/tests/10-clear-resources/03-delete-service-acc.ts +++ b/e2e/cypress/tests/10-clear-resources/06-delete-service-acc.ts @@ -16,7 +16,6 @@ describe('Create API Spec', () => { before(() => { cy.visit('/') - cy.deleteAllCookies() cy.reload() }) @@ -24,14 +23,17 @@ describe('Create API Spec', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespace) + }) }) }) 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 58bbff630..6ae592157 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 @@ -13,18 +13,19 @@ describe('Get the user session token to pass it as authorization token to make t before(() => { cy.visit('/') - // cy.deleteAllCookies() + cy.deleteAllCookies() cy.reload() }) beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.getUserSessionTokenValue(namespace).then((value) => { userSession = value }) @@ -103,11 +104,12 @@ describe('Verify the Activity filter for users', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('activates new namespace', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { home.useNamespace(namespace) }) }) 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 d1535a836..c6fd120ab 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 @@ -81,6 +81,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates api owner', () => { cy.get('@apiowner').then(({ user }: any) => { @@ -89,7 +90,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t }) it('Activates namespace for client credential flow tests', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { + cy.get('@common-testdata').then(({ clientCredentials }: any) => { nameSpace = clientCredentials.namespace home.useNamespace(clientCredentials.namespace) cy.get('@login').then(function (xhr: any) { 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 f30f59aa4..a5488bb92 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 @@ -21,12 +21,13 @@ describe('Create API Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.fixture('api').as('api') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -45,7 +46,7 @@ describe('Create API Spec', () => { assert.isNotNaN(response.stdout) namespace = response.stdout cy.replaceWordInJsonObject('ns.permission', 'ns.' + namespace, 'service-permission-gwa.yml') - cy.updateJsonValue('apiowner.json', 'checkPermission.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'checkPermission.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); diff --git a/e2e/cypress/tests/12-access-permission/02-team-access.cy.ts b/e2e/cypress/tests/12-access-permission/02-team-access.cy.ts index 0e0aa0f14..c8bb5898f 100644 --- a/e2e/cypress/tests/12-access-permission/02-team-access.cy.ts +++ b/e2e/cypress/tests/12-access-permission/02-team-access.cy.ts @@ -16,14 +16,17 @@ describe('Team Access Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(checkPermission.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(checkPermission.namespace) + }) }) }) @@ -36,7 +39,7 @@ describe('Team Access Spec', () => { }) it('Grant permission to Janis (API Owner)', () => { - cy.get('@apiowner').then(({checkPermission}: any) => { + cy.get('@apiowner').then(({ checkPermission }: any) => { cy.visit(na.path) na.clickGrantUserAccessButton() na.grantPermission(checkPermission.grantPermission.Janis) diff --git a/e2e/cypress/tests/12-access-permission/04-access-manager.cy.ts b/e2e/cypress/tests/12-access-permission/04-access-manager.cy.ts index 47a0a6668..9a22515ea 100644 --- a/e2e/cypress/tests/12-access-permission/04-access-manager.cy.ts +++ b/e2e/cypress/tests/12-access-permission/04-access-manager.cy.ts @@ -18,16 +18,19 @@ describe('Grant Access Manager Role', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { cy.login(user.credentials.username, user.credentials.password) cy.log('Logged in!') home.useNamespace(checkPermission.namespace) }) }) + }) it('Grant "Access.Manager" access to Mark (access manager)', () => { cy.get('@apiowner').then(({ checkPermission }: any) => { @@ -63,15 +66,15 @@ describe('Verify that Mark is able to view the pending request', () => { cy.preserveCookies() cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({checkPermission}: any) => { + cy.get('@common-testdata').then(({checkPermission}: any) => { cy.visit(login.path) cy.login(user.credentials.username, user.credentials.password) cy.log('Logged in!') - debugger home.useNamespace(checkPermission.namespace) cy.visit(mp.path) }) diff --git a/e2e/cypress/tests/12-access-permission/05-namespace-manage.cy.ts b/e2e/cypress/tests/12-access-permission/05-namespace-manage.cy.ts index cb489c8e8..79fa205cc 100644 --- a/e2e/cypress/tests/12-access-permission/05-namespace-manage.cy.ts +++ b/e2e/cypress/tests/12-access-permission/05-namespace-manage.cy.ts @@ -21,14 +21,17 @@ describe('Grant Namespace Manage Role', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('Authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(checkPermission.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(checkPermission.namespace) + }) }) }) @@ -69,11 +72,12 @@ describe('Verify that Wendy is able to see all the options for the Namespace', ( cy.preserveCookies() cy.fixture('credential-issuer').as('credential-issuer') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Wendy (Credential-Issuer)', () => { cy.get('@credential-issuer').then(({ user }: any) => { - cy.get('@apiowner').then(({ checkPermission }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { cy.visit(login.path) cy.login(user.credentials.username, user.credentials.password) cy.log('Logged in!') diff --git a/e2e/cypress/tests/12-access-permission/06-credential-issuer.cy.ts b/e2e/cypress/tests/12-access-permission/06-credential-issuer.cy.ts index 24efcdd65..5903a618a 100644 --- a/e2e/cypress/tests/12-access-permission/06-credential-issuer.cy.ts +++ b/e2e/cypress/tests/12-access-permission/06-credential-issuer.cy.ts @@ -20,14 +20,17 @@ describe('Grant Credential Issuer Role', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('Authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(checkPermission.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(checkPermission.namespace) + }) }) }) @@ -67,11 +70,12 @@ describe('Verify that Wendy is able to generate authorization profile', () => { cy.preserveCookies() cy.fixture('credential-issuer').as('credential-issuer') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Wendy (Credential-Issuer)', () => { cy.get('@credential-issuer').then(({ user }: any) => { - cy.get('@apiowner').then(({ checkPermission }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { cy.visit(login.path) cy.login(user.credentials.username, user.credentials.password) cy.log('Logged in!') diff --git a/e2e/cypress/tests/12-access-permission/07-namespace-view.cy.ts b/e2e/cypress/tests/12-access-permission/07-namespace-view.cy.ts index f97827918..05f1621cc 100644 --- a/e2e/cypress/tests/12-access-permission/07-namespace-view.cy.ts +++ b/e2e/cypress/tests/12-access-permission/07-namespace-view.cy.ts @@ -20,14 +20,17 @@ describe('Grant Namespace View Role to Mark', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('Authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(checkPermission.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(checkPermission.namespace) + }) }) }) @@ -66,11 +69,12 @@ describe('Verify that Mark is unable to create service account', () => { cy.fixture('credential-issuer').as('credential-issuer') cy.fixture('access-manager').as('access-manager') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Mark', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ checkPermission }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { cy.visit(login.path) cy.login(user.credentials.username, user.credentials.password) cy.log('Logged in!') diff --git a/e2e/cypress/tests/12-access-permission/08-gateway-config.cy.ts b/e2e/cypress/tests/12-access-permission/08-gateway-config.cy.ts index c10b06cb7..ec94ec380 100644 --- a/e2e/cypress/tests/12-access-permission/08-gateway-config.cy.ts +++ b/e2e/cypress/tests/12-access-permission/08-gateway-config.cy.ts @@ -23,13 +23,16 @@ describe('Grant Gateway Config Role to Wendy', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - home.useNamespace(checkPermission.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(checkPermission.namespace) + }) }) }) @@ -68,12 +71,13 @@ describe('Verify that Wendy is able to generate authorization profile', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('credential-issuer').as('credential-issuer') + cy.fixture('common-testdata').as('common-testdata') cy.fixture('apiowner').as('apiowner') }) it('Authenticates Wendy (Credential-Issuer)', () => { cy.get('@credential-issuer').then(({ user }: any) => { - cy.get('@apiowner').then(({ checkPermission }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { cy.visit(login.path) cy.login(user.credentials.username, user.credentials.password) cy.log('Logged in!') @@ -84,7 +88,7 @@ describe('Verify that Wendy is able to generate authorization profile', () => { }) it('Verify that GWA API allows user to publish the API to Kong gateway', () => { - cy.get('@apiowner').then(({ checkPermission }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { cy.publishApi('service-permission.yml', checkPermission.namespace).then((response: any) => { expect(response.stdout).to.contain('Sync successful'); }) 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 0c8b8d685..b607367d5 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 @@ -6,7 +6,7 @@ import HomePage from '../../pageObjects/home' describe('Verify Content Publish Permission', () => { const login = new LoginPage() const home = new HomePage() - let token : any + let token: any before(() => { cy.visit('/') @@ -18,13 +18,16 @@ describe('Verify Content Publish Permission', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, checkPermission }: any) => { - cy.login(user.credentials.username, user.credentials.password) - home.useNamespace(checkPermission.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(checkPermission.namespace) + }) }) }) diff --git a/e2e/cypress/tests/13-namespace-preview-mode/01-create-api.cy.ts b/e2e/cypress/tests/13-namespace-preview-mode/01-create-api.cy.ts index c1c977618..42e545761 100644 --- a/e2e/cypress/tests/13-namespace-preview-mode/01-create-api.cy.ts +++ b/e2e/cypress/tests/13-namespace-preview-mode/01-create-api.cy.ts @@ -22,11 +22,12 @@ describe('Create API Spec', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -44,7 +45,7 @@ describe('Create API Spec', () => { cy.exec('gwa namespace create --host ' + cleanedUrl + ' --scheme http', { timeout: 3000, failOnNonZeroExit: false }).then((response) => { assert.isNotNaN(response.stdout) namespace = response.stdout - cy.updateJsonValue('apiowner.json', 'namespacePreview.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'namespacePreview.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); diff --git a/e2e/cypress/tests/13-namespace-preview-mode/02-namespace-preview-mode.cy.ts b/e2e/cypress/tests/13-namespace-preview-mode/02-namespace-preview-mode.cy.ts index cc8dc9a19..2d73b0753 100644 --- a/e2e/cypress/tests/13-namespace-preview-mode/02-namespace-preview-mode.cy.ts +++ b/e2e/cypress/tests/13-namespace-preview-mode/02-namespace-preview-mode.cy.ts @@ -26,13 +26,16 @@ describe('Verify Products when namespace in Preview Mode', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Janis (api owner)', () => { - cy.get('@apiowner').then(({ user, namespacePreview }: any) => { - cy.login(user.credentials.username, user.credentials.password) - cy.log('Logged in!') - home.useNamespace(namespacePreview.namespace) + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ namespacePreview }: any) => { + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + home.useNamespace(namespacePreview.namespace) + }) }) }) @@ -50,7 +53,7 @@ describe('Verify Products when namespace in Preview Mode', () => { }) it('Navigate to Your Product tab in API Directory page', () => { - apiDir.navigateToYourProduct() + apiDir.navigateToYourProduct() }) it('Verify that the banner for Preview mode is displayed', () => { diff --git a/e2e/cypress/tests/14-org-assignment/01-client-cred-team-access.ts b/e2e/cypress/tests/14-org-assignment/01-client-cred-team-access.ts index 3d491297b..29fc44462 100644 --- a/e2e/cypress/tests/14-org-assignment/01-client-cred-team-access.ts +++ b/e2e/cypress/tests/14-org-assignment/01-client-cred-team-access.ts @@ -30,11 +30,12 @@ describe('Add Organization to publish API', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -52,7 +53,7 @@ describe('Add Organization to publish API', () => { cy.exec('gwa namespace create --host ' + cleanedUrl + ' --scheme http', { timeout: 3000, failOnNonZeroExit: false }).then((response) => { assert.isNotNaN(response.stdout) namespace = response.stdout - cy.updateJsonValue('apiowner.json', 'orgAssignment.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'orgAssignment.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); @@ -151,6 +152,7 @@ describe('Org Admin approves the request', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('product-owner').as('product-owner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) @@ -199,6 +201,7 @@ describe('Activate the API to make it visible in API Directory', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) diff --git a/e2e/cypress/tests/14-org-assignment/02-multiple-org-admin.ts b/e2e/cypress/tests/14-org-assignment/02-multiple-org-admin.ts index 4a04424c9..1abe74e2a 100644 --- a/e2e/cypress/tests/14-org-assignment/02-multiple-org-admin.ts +++ b/e2e/cypress/tests/14-org-assignment/02-multiple-org-admin.ts @@ -23,6 +23,7 @@ describe('Give a user org admin access at organization level', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') cy.fixture('admin').as('admin') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Admin owner', () => { @@ -78,12 +79,13 @@ describe('Multiple Org Adming for the organization', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -101,7 +103,7 @@ describe('Multiple Org Adming for the organization', () => { cy.exec('gwa namespace create --host ' + cleanedUrl + ' --scheme http', { timeout: 3000, failOnNonZeroExit: false }).then((response) => { assert.isNotNaN(response.stdout) namespace = response.stdout - cy.updateJsonValue('apiowner.json', 'orgAssignment.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'orgAssignment.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); diff --git a/e2e/cypress/tests/14-org-assignment/03-multiple-org-admin-org-unit.ts b/e2e/cypress/tests/14-org-assignment/03-multiple-org-admin-org-unit.ts index 2d10435b5..2fa19e811 100644 --- a/e2e/cypress/tests/14-org-assignment/03-multiple-org-admin-org-unit.ts +++ b/e2e/cypress/tests/14-org-assignment/03-multiple-org-admin-org-unit.ts @@ -25,6 +25,7 @@ describe('Give a user org admin access at organization unit level', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') cy.fixture('admin').as('admin') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Admin owner', () => { @@ -95,11 +96,12 @@ describe('Multiple Org Admin for the organization', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -117,7 +119,7 @@ describe('Multiple Org Admin for the organization', () => { cy.exec('gwa namespace create --host ' + cleanedUrl + ' --scheme http', { timeout: 3000, failOnNonZeroExit: false }).then((response) => { assert.isNotNaN(response.stdout) namespace = response.stdout - cy.updateJsonValue('apiowner.json', 'orgAssignment.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'orgAssignment.namespace', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) }); 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 d47a5fa60..e85a6fff4 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 @@ -22,11 +22,12 @@ describe('Create API Spec', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -44,7 +45,7 @@ describe('Create API Spec', () => { cy.exec('gwa namespace create --host ' + cleanedUrl + ' --scheme http', { timeout: 3000, failOnNonZeroExit: false }).then((response) => { assert.isNotNaN(response.stdout) namespace = response.stdout - cy.updateJsonValue('apiowner.json', 'apiTest.namespace', namespace) + cy.updateJsonValue('common-testdata.json', 'apiTest.namespace', namespace) cy.updateJsonValue('api.json', 'organization.expectedNamespace.name', namespace) // cy.updateJsonValue('apiowner.json', 'clientCredentials.clientIdSecret.product.environment.name.config.serviceName', 'cc-service-for-' + namespace) cy.executeCliCommand("gwa config set --namespace " + namespace) 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 5b9649af9..105dba5ad 100644 --- a/e2e/cypress/tests/15-aps-api/02-organization.cy.ts +++ b/e2e/cypress/tests/15-aps-api/02-organization.cy.ts @@ -56,7 +56,7 @@ describe('Verify /Organization/{Org} end point', () => { 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("Organization not found.") + expect(response.body.message).to.be.equal("Validation Failed") }) }) }) @@ -77,11 +77,12 @@ describe('Get the user session token', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { userSession = value }) @@ -170,7 +171,6 @@ describe('Get the Namespace associated with the organization', () => { cy.get('@api').then(({ organization }: any) => { expectedResponse = organization.expectedNamespace // assert.isTrue(Cypress._.isEqual(response, expectedResponse)) - debugger cy.compareJSONObjects(response, expectedResponse, true) }) }) @@ -186,6 +186,7 @@ describe('Delete the Namespace associated with the organization', () => { beforeEach(() => { cy.fixture('api').as('api') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Prepare the Request Specification for the API', () => { @@ -196,7 +197,7 @@ 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('@apiowner').then(({ namespace }: any) => { + 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) 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 9f749ba12..a0428633f 100644 --- a/e2e/cypress/tests/15-aps-api/03-documentation.cy.ts +++ b/e2e/cypress/tests/15-aps-api/03-documentation.cy.ts @@ -18,11 +18,12 @@ describe('Get the user session token', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { userSession = value namespace = apiTest.namespace diff --git a/e2e/cypress/tests/15-aps-api/04-keycloak-shared-IDP-config.cy.ts b/e2e/cypress/tests/15-aps-api/04-keycloak-shared-IDP-config.cy.ts index ed2fd5f48..b6d0739a2 100644 --- a/e2e/cypress/tests/15-aps-api/04-keycloak-shared-IDP-config.cy.ts +++ b/e2e/cypress/tests/15-aps-api/04-keycloak-shared-IDP-config.cy.ts @@ -28,6 +28,7 @@ describe('Apply Shared IDP config at Keycloak user group', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('state/regen').as('regen') cy.fixture('admin').as('admin') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Admin owner', () => { @@ -42,7 +43,7 @@ describe('Apply Shared IDP config at Keycloak user group', () => { }) it('Edit the namespace from the tree view', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.contains(apiTest.namespace).click() userGroups.clickOnEditButton() }) 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 5417e4c65..256295862 100644 --- a/e2e/cypress/tests/15-aps-api/05-authorizationProfiles.cy.ts +++ b/e2e/cypress/tests/15-aps-api/05-authorizationProfiles.cy.ts @@ -21,11 +21,12 @@ describe('Get the user session token', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { userSession = value namespace = apiTest.namespace @@ -115,6 +116,7 @@ describe('API Tests for Authorization Profiles created with inheritFrom attribut beforeEach(() => { cy.fixture('api').as('api') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Prepare the Request Specification to create a shared IDP profile', () => { @@ -126,7 +128,7 @@ 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('@apiowner').then(({ apiTest }: any) => { + 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) }) @@ -142,7 +144,7 @@ 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('@apiowner').then(({ apiTest }: any) => { + 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") @@ -151,7 +153,7 @@ describe('API Tests for Authorization Profiles created with inheritFrom attribut }) it('Get list of authorization profile and verify the success code in the response', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + 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 @@ -178,6 +180,7 @@ describe('Published a shared authorization profile', () => { beforeEach(() => { cy.fixture('api').as('api') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Prepare the Request Specification to create a shared IDP profile', () => { @@ -189,7 +192,7 @@ describe('Published a shared authorization profile', () => { }) it('Create a shared credential issuer', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + 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") @@ -198,7 +201,7 @@ describe('Published a shared authorization profile', () => { }) it('Get list of authorization profile and verify the success code in the response', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + 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 @@ -210,7 +213,7 @@ describe('Published a shared authorization profile', () => { cy.logout() cy.clearLocalStorage({ log: true }) cy.deleteAllCookies() - }) + }) }) @@ -230,10 +233,11 @@ describe('Deleted shared auth profile', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') }) it('Authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { userSession = value namespace = apiTest.namespace @@ -252,6 +256,12 @@ describe('Deleted shared auth profile', () => { }) }) + it('Verify the confirmation message to delete the consumer', () => { + cy.wait(2000) + cy.contains('This action cannot be undone').should('exist') + cy.contains('Yes, Delete').click() + }) + after(() => { cy.logout() cy.clearLocalStorage({ log: true }) 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 0b9053cb1..b76fcdbd7 100644 --- a/e2e/cypress/tests/15-aps-api/06-products.cy.ts +++ b/e2e/cypress/tests/15-aps-api/06-products.cy.ts @@ -21,17 +21,20 @@ describe('Get the user session token to check ', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { cy.getUserSession().then(() => { - cy.get('@apiowner').then(({ user, apiTest }: any) => { - cy.login(user.credentials.username, user.credentials.password) - home.useNamespace(apiTest.namespace) - namespace = apiTest.namespace - cy.get('@login').then(function (xhr: any) { - userSession = xhr.response.headers['x-auth-request-access-token'] + cy.get('@apiowner').then(({ user }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(apiTest.namespace) + namespace = apiTest.namespace + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) }) }) }) @@ -76,7 +79,6 @@ describe('API Tests for Updating Products', () => { response = res.body[index] productID = res.body[index].appId envID = res.body[index].environments[0].appId - debugger }) }) }) @@ -103,11 +105,12 @@ describe('Verify that created Product is displayed in UI', () => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') cy.fixture('api').as('api') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { home.useNamespace(apiTest.namespace) userSession = value 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 19309fcd2..eec37feeb 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 @@ -17,11 +17,12 @@ describe('Get the user session token', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { userSession = value }) @@ -41,6 +42,7 @@ describe('API Tests for Updating dataset', () => { beforeEach(() => { cy.fixture('api').as('api') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Prepare the Request Specification for the API', () => { @@ -60,7 +62,7 @@ describe('API Tests for Updating dataset', () => { }) it('Get the resource (/organization/{org}/datasets/{name}) and verify the success code in the response', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + 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) @@ -77,7 +79,7 @@ describe('API Tests for Updating dataset', () => { }) it('Put the resource (/namespaces/{ns}/datasets/{name}) and verify the success code in the response', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + 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) @@ -87,7 +89,7 @@ describe('API Tests for Updating dataset', () => { }) it('Get the resource (/namespaces/{ns}/datasets/{name}) and verify the success code in the response', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + 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) @@ -160,7 +162,7 @@ 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('@apiowner').then(({ apiTest }: any) => { + 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) @@ -171,7 +173,7 @@ 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('@apiowner').then(({ namespace }: any) => { + 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) @@ -190,7 +192,7 @@ 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('@apiowner').then(({ namespace }: any) => { + 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) @@ -201,7 +203,7 @@ 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('@apiowner').then(({ namespace }: any) => { + 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]) 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 8a050f7e4..6d3363627 100644 --- a/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts +++ b/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts @@ -18,11 +18,12 @@ describe('Get the user session token to pass it as authorization token to make t beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace).then((value) => { userSession = value }) @@ -123,6 +124,7 @@ describe('API Tests for Namespace Summary', () => { beforeEach(() => { cy.fixture('api').as('api') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('Prepare the Request Specification for the API', () => { @@ -133,7 +135,7 @@ describe('API Tests for Namespace Summary', () => { }) it('Get the resource for namespace summary and verify the success code in the response', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + 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) diff --git a/e2e/cypress/tests/16-gwa-cli/01-cli-commands.ts b/e2e/cypress/tests/16-gwa-cli/01-cli-commands.ts index fb4bd7390..28be88935 100644 --- a/e2e/cypress/tests/16-gwa-cli/01-cli-commands.ts +++ b/e2e/cypress/tests/16-gwa-cli/01-cli-commands.ts @@ -24,11 +24,12 @@ describe('Verify CLI commands', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) diff --git a/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config.ts b/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config.ts index 7ced10b71..77e5cdc45 100644 --- a/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config.ts +++ b/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config.ts @@ -52,7 +52,7 @@ describe('Verify CLI commands for generate/apply config', () => { }) it('Check gwa command to apply generated config', () => { - cy.executeCliCommand('gwa apply').then((response) => { + cy.executeCliCommand('gwa apply -i gw-config.yml').then((response) => { let wordOccurrences = (response.stdout.match(/\bcreated\b/g) || []).length; expect(wordOccurrences).to.equal(3) namespace = response.stdout.split('\n')[0] @@ -60,11 +60,11 @@ describe('Verify CLI commands for generate/apply config', () => { }); }) - it('Check gwa command to generate config for kong httpbin template', () => { - cy.executeCliCommand('gwa generate-config --template kong-httpbin --service my-service --upstream https://httpbin.org --org ministry-of-health --org-unit planning-and-innovation-division').then((response) => { - assert.equal(response.stdout, "File gw-config.yml created") - }); - }) + // it('Check gwa command to generate config for kong httpbin template', () => { + // cy.executeCliCommand('gwa generate-config --template kong-httpbin --service my-service --upstream https://httpbin.org --org ministry-of-health --org-unit planning-and-innovation-division').then((response) => { + // assert.equal(response.stdout, "File gw-config.yml created") + // }); + // }) it('activates new namespace', () => { home.useNamespace(namespace) diff --git a/e2e/cypress/tests/17-delete-application/03-delete-application-with-approved-request.cy.ts b/e2e/cypress/tests/17-delete-application/03-delete-application-with-approved-request.cy.ts index ad189ce02..e94e77f2e 100644 --- a/e2e/cypress/tests/17-delete-application/03-delete-application-with-approved-request.cy.ts +++ b/e2e/cypress/tests/17-delete-application/03-delete-application-with-approved-request.cy.ts @@ -72,12 +72,13 @@ describe('Approve Pending Request Spec', () => { cy.fixture('apiowner').as('apiowner') cy.fixture('developer').as('developer') cy.fixture('state/store').as('store') + cy.fixture('common-testdata').as('common-testdata') // cy.visit(login.path) }) it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user }: any) => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { cy.login(user.credentials.username, user.credentials.password) home.useNamespace(namespace); }) @@ -117,6 +118,7 @@ describe('Delete application which has approved request spec', () => { cy.preserveCookies() cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') }) it('authenticates Harley (developer)', () => { diff --git a/e2e/cypress/tests/17-delete-application/04-delete-namespace-gwa.ts b/e2e/cypress/tests/17-delete-application/04-delete-namespace-gwa.ts index 219282ac6..ac18faa89 100644 --- a/e2e/cypress/tests/17-delete-application/04-delete-namespace-gwa.ts +++ b/e2e/cypress/tests/17-delete-application/04-delete-namespace-gwa.ts @@ -14,11 +14,12 @@ describe('Verify namespace delete using gwa command', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('common-testdata').as('common-testdata') cy.visit(login.path) }) it('Authenticates Janis (api owner) to get the user session token', () => { - cy.get('@apiowner').then(({ apiTest }: any) => { + cy.get('@common-testdata').then(({ apiTest }: any) => { cy.getUserSessionTokenValue(apiTest.namespace, false).then((value) => { userSession = value }) @@ -51,26 +52,20 @@ describe('Verify namespace delete using gwa command', () => { }) it('Check gwa namespace destroy command for the namespace associated with services', () => { - cy.get('@apiowner').then(({ namespace }: any) => { + cy.get('@common-testdata').then(({ namespace }: any) => { _namespace = namespace cy.executeCliCommand('gwa config set --namespace ' + namespace).then((response) => { expect(response.stdout).to.contain("Config settings saved") cy.executeCliCommand('gwa namespace destroy').then((response) => { - expect(response.stderr).to.contain('services have been configured in this namespace'); + expect(response.stderr).to.contain('Error: Validation Failed'); }); }) }) }) - it('Check gwa namespace destroy command for hard deleting namespace', () => { + it('Check validation if any consumer is associated with namespace for hard deleting the namespace', () => { cy.executeCliCommand('gwa namespace destroy --force').then((response) => { - expect(response.stdout).to.contain('Namespace destroyed: ' + _namespace); - }); - }) - - it('Check that deleted namespace does not display in gwa namespace list command', () => { - cy.executeCliCommand('gwa namespace list').then((response) => { - expect(response.stdout).not.to.contain(_namespace); + expect(response.stderr).to.contain('Error: Validation Failed'); }); }) diff --git a/e2e/package.json b/e2e/package.json index 1726f71fc..9ed7e4bb4 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -7,7 +7,7 @@ "license": "MIT", "scripts": { "cy:open": "cypress open --config-file=cypress.config.ts", - "cy:run": "npx cypress run --headed --config-file cypress.config.ts --env TransferProtocol='http' --browser chrome", + "cy:run": "npx cypress run --headed --config-file cypress.config.ts --env TransferProtocol='http' --browser edge", "cy:run:rcd": "npm run cy:run -- --record", "moch:json": "mochawesome-merge -f results/*.json -o results/bcgov-aps-e2e-report.json", "cy:chromium": "cypress open --browser /usr/bin/chromium", diff --git a/src/api-openapi.js b/src/api-openapi.js index b2d1f99a2..59a91fb0a 100644 --- a/src/api-openapi.js +++ b/src/api-openapi.js @@ -12,6 +12,7 @@ var options = { const { Register } = require('./controllers/ioc/registry'); const { UnauthorizedError } = require('express-jwt'); const { AssertionError } = require('assert'); +const { BatchSyncException } = require('./batch/types'); class ApiOpenapiApp { constructor() {} @@ -88,7 +89,7 @@ class ApiOpenapiApp { message: err.message, }); } else if (err instanceof ValidateError) { - console.warn(`Caught Validation Error for ${req.path}:`, err.fields); + logger.warn(`Caught Validation Error for ${req.path}:`, err.fields); return res.status(422).json({ message: 'Validation Failed', details: err?.fields, @@ -106,6 +107,16 @@ class ApiOpenapiApp { logger.warn('Failed to parse error message %s', e); } return res.status(422).json(response); + } else if (err instanceof SyntaxError) { + logger.error(err); + logger.error('Syntax Error PATH: %s', req.path); + return res.status(422).json({ + message: 'Syntax Error Parsing JSON', + }); + } else if (err instanceof BatchSyncException) { + logger.error(err); + logger.error('BatchSync PATH: %s', req.path); + return res.status(400).json(err.result); } else if (err instanceof Error) { logger.error(err); logger.error('Error PATH: %s', req.path); diff --git a/src/api-proxy.js b/src/api-proxy.js index 8540c1521..a12b26210 100644 --- a/src/api-proxy.js +++ b/src/api-proxy.js @@ -15,7 +15,10 @@ class ApiProxyApp { target: this._gwaApiUrl, changeOrigin: true, logLevel: 'debug', - pathRewrite: { '^/gw/api/': '/v2/' }, + pathRewrite: { + '^/gw/api/v2/': '/v2/', + '^/gw/api/v3/': '/v3/', + }, onProxyReq: (proxyReq, req) => { //console.log(req.headers) // proxyReq.removeHeader("cookie"); diff --git a/src/batch/feed-worker.ts b/src/batch/feed-worker.ts index b33db0978..b2e368a3c 100644 --- a/src/batch/feed-worker.ts +++ b/src/batch/feed-worker.ts @@ -13,7 +13,7 @@ import { } from './transformations'; import { handleNameChange, handleUsernameChange } from './hooks'; import YAML from 'js-yaml'; -import { BatchResult } from './types'; +import { BatchResult, BatchSyncException } from './types'; import { BatchService, BatchWhereClause, @@ -288,6 +288,20 @@ export const getRecord = async function ( ); }; +export const syncRecordsThrowErrors = async function ( + context: any, + feedEntity: string, + eid: string, + json: any, + children = false +): Promise { + const result = await syncRecords(context, feedEntity, eid, json, children); + if (result.status !== 200) { + throw new BatchSyncException(result); + } + return result; +}; + export const syncRecords = async function ( context: any, feedEntity: string, @@ -399,7 +413,12 @@ export const syncRecords = async function ( } } catch (ex) { logger.error('Caught exception %s', ex); - return { status: 400, result: 'create-failed', childResults }; + return { + status: 400, + result: 'create-failed', + reason: ex.message, + childResults, + }; } } else { try { @@ -516,7 +535,12 @@ export const syncRecords = async function ( } } catch (ex) { logger.error('Caught exception %s', ex); - return { status: 400, result: 'update-failed', childResults }; + return { + status: 400, + result: 'update-failed', + reason: ex.message, + childResults, + }; } } }; diff --git a/src/batch/transformations/connectOne.ts b/src/batch/transformations/connectOne.ts index d9921039a..ca95335e0 100644 --- a/src/batch/transformations/connectOne.ts +++ b/src/batch/transformations/connectOne.ts @@ -39,7 +39,7 @@ export async function connectOne( logger.error( `Lookup failed for ${transformInfo['list']} ${transformInfo['refKey']}!` ); - throw Error('Failed to find ' + value + ' in ' + transformInfo['list']); + throw Error(`Record not found [${_fieldKey}] ${value}`); } else if ( currentData != null && currentData[_fieldKey] && diff --git a/src/batch/types.ts b/src/batch/types.ts index 81601e87a..770cd390e 100644 --- a/src/batch/types.ts +++ b/src/batch/types.ts @@ -6,3 +6,11 @@ export interface BatchResult { ownedBy?: string; childResults?: BatchResult[]; } + +export class BatchSyncException extends Error { + result: BatchResult; + constructor(result: BatchResult) { + super(); + this.result = result; + } +} diff --git a/src/controllers/ioc/assert.ts b/src/controllers/ioc/assert.ts new file mode 100644 index 000000000..5c712a5ed --- /dev/null +++ b/src/controllers/ioc/assert.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { ValidateError, FieldErrors } from 'tsoa'; + +export function assertEqual( + condition: any, + match: any, + field: string, + message: string +) { + try { + assert.strictEqual(condition, match, message); + } catch (e) { + var fieldErrors: FieldErrors = {}; + fieldErrors[field] = { + message, + }; + throw new ValidateError(fieldErrors, ''); + } +} diff --git a/src/controllers/v2/ContentController.ts b/src/controllers/v2/ContentController.ts index b8f4327ef..501982517 100644 --- a/src/controllers/v2/ContentController.ts +++ b/src/controllers/v2/ContentController.ts @@ -20,7 +20,7 @@ import { parseJsonString, removeEmpty, removeKeys, - syncRecords, + syncRecordsThrowErrors, } from '../../batch/feed-worker'; import express from 'express'; import multer from 'multer'; @@ -59,7 +59,7 @@ export class ContentController extends Controller { @Request() request: any ): Promise { body['namespace'] = ns; - return await syncRecords( + return await syncRecordsThrowErrors( this.keystone.createContext(request), 'Content', body['externalLink'], diff --git a/src/controllers/v2/DatasetController.ts b/src/controllers/v2/DatasetController.ts index a295cd80f..f0882c6cb 100644 --- a/src/controllers/v2/DatasetController.ts +++ b/src/controllers/v2/DatasetController.ts @@ -17,7 +17,7 @@ import { parseJsonString, removeEmpty, removeKeys, - syncRecords, + syncRecordsThrowErrors, transformAllRefID, } from '../../batch/feed-worker'; import { Dataset, DraftDataset } from './types'; @@ -56,7 +56,7 @@ export class DatasetController extends Controller { // - isInCatalog must be false (OrgDataset should only be updating this) removeKeys(body, ['isInDraft', 'isInCatalog']); - return await syncRecords( + return await syncRecordsThrowErrors( this.keystone.createContext(request), 'DraftDataset', request.body['name'], diff --git a/src/controllers/v2/IssuerController.ts b/src/controllers/v2/IssuerController.ts index db1b4770a..98f414bda 100644 --- a/src/controllers/v2/IssuerController.ts +++ b/src/controllers/v2/IssuerController.ts @@ -14,7 +14,7 @@ import { import { KeystoneService } from '../ioc/keystoneInjector'; import { inject, injectable } from 'tsyringe'; import { - syncRecords, + syncRecordsThrowErrors, getRecords, removeEmpty, removeKeys, @@ -51,7 +51,7 @@ export class IssuerController extends Controller { @Body() body: CredentialIssuer, @Request() request: any ): Promise { - return await syncRecords( + return await syncRecordsThrowErrors( this.keystone.createContext(request), 'CredentialIssuer', body['name'], diff --git a/src/controllers/v2/OrgDatasetController.ts b/src/controllers/v2/OrgDatasetController.ts index af6a2613c..698ea58c4 100644 --- a/src/controllers/v2/OrgDatasetController.ts +++ b/src/controllers/v2/OrgDatasetController.ts @@ -15,7 +15,7 @@ import { strict as assert } from 'assert'; import { KeystoneService } from '../ioc/keystoneInjector'; import { inject, injectable } from 'tsyringe'; import { - syncRecords, + syncRecordsThrowErrors, getRecords, parseJsonString, removeEmpty, @@ -95,7 +95,7 @@ export class OrgDatasetController extends Controller { @Request() request: any ): Promise { assert.strictEqual(org, body['organization'], 'Organization Mismatch'); - return await syncRecords( + return await syncRecordsThrowErrors( this.keystone.createContext(request, true), 'DraftDataset', body['name'], diff --git a/src/controllers/v2/OrganizationController.ts b/src/controllers/v2/OrganizationController.ts index e7e576c0b..26a3652c9 100644 --- a/src/controllers/v2/OrganizationController.ts +++ b/src/controllers/v2/OrganizationController.ts @@ -12,8 +12,8 @@ import { Get, Tags, } from 'tsoa'; -import { strict as assert } from 'assert'; import { KeystoneService } from '../ioc/keystoneInjector'; +import { assertEqual } from '../ioc/assert'; import { inject, injectable } from 'tsyringe'; import { syncRecords, @@ -70,9 +70,10 @@ export class OrganizationController extends Controller { public async listOrganizationUnits(@Path() org: string): Promise { const orgs = await getOrganizations(this.keystone.sudo()); const match = orgs.filter((o) => o.name === org).pop(); - assert.strictEqual( + assertEqual( typeof match === 'undefined', false, + 'org', 'Organization not found.' ); @@ -128,9 +129,10 @@ export class OrganizationController extends Controller { @Body() body: GroupMembership ): Promise { // must match either the 'name' or one of the parent nodes - assert.strictEqual( + assertEqual( org === body.name || isParent(body.parent, org), true, + 'org', 'Organization mismatch' ); @@ -172,9 +174,10 @@ export class OrganizationController extends Controller { ): Promise<{ result: string }> { const ctx = this.keystone.sudo(); const orgLookup = await getOrganizationUnit(ctx, orgUnit); - assert.strictEqual( + assertEqual( orgLookup != null && orgLookup.name === org, true, + 'org', 'Invalid Organization' ); @@ -204,9 +207,10 @@ export class OrganizationController extends Controller { ): Promise<{ result: string }> { const ctx = this.keystone.sudo(); const orgLookup = await getOrganizationUnit(ctx, orgUnit); - assert.strictEqual( + assertEqual( orgLookup != null && orgLookup.name === org, true, + 'org', 'Invalid Organization' ); diff --git a/src/controllers/v2/ProductController.ts b/src/controllers/v2/ProductController.ts index 605919c83..81340dc13 100644 --- a/src/controllers/v2/ProductController.ts +++ b/src/controllers/v2/ProductController.ts @@ -17,7 +17,7 @@ import { import { KeystoneService } from '../ioc/keystoneInjector'; import { inject, injectable } from 'tsyringe'; import { - syncRecords, + syncRecordsThrowErrors, getRecords, removeEmpty, removeKeys, @@ -64,7 +64,7 @@ export class ProductController extends Controller { @Body() body: Product, @Request() request: any ): Promise { - return await syncRecords( + return await syncRecordsThrowErrors( this.keystone.createContext(request), 'Product', body['appId'], diff --git a/src/lists/CredentialIssuer.js b/src/lists/CredentialIssuer.js index 6147b2c5b..8f62cce4e 100644 --- a/src/lists/CredentialIssuer.js +++ b/src/lists/CredentialIssuer.js @@ -250,11 +250,13 @@ module.exports = { }, afterDelete: async function ({ existingItem, context }) { - await DeleteClientsFromSharedIdP( - context, - existingItem.clientId, - `${existingItem.inheritFrom}` - ); + if (Boolean(existingItem.inheritFrom)) { + await DeleteClientsFromSharedIdP( + context, + existingItem.clientId, + `${existingItem.inheritFrom}` + ); + } await new StructuredActivityService( context, diff --git a/src/lists/Metric.js b/src/lists/Metric.js index b1ed549b0..d47d30f09 100644 --- a/src/lists/Metric.js +++ b/src/lists/Metric.js @@ -10,6 +10,7 @@ module.exports = { name: { type: Text, isRequired: true, + isUnique: true, }, query: { type: Text, diff --git a/src/lists/extensions/ConsumerProducts.ts b/src/lists/extensions/ConsumerProducts.ts index 32d51ccd0..1fe020368 100644 --- a/src/lists/extensions/ConsumerProducts.ts +++ b/src/lists/extensions/ConsumerProducts.ts @@ -6,6 +6,7 @@ import { getFilteredNamespaceConsumers, getNamespaceConsumerAccess, grantAccessToConsumer, + enforceCheckForNoPendingRequests, revokeAllConsumerAccess, revokeAccessFromConsumer, saveConsumerLabels, @@ -227,6 +228,11 @@ module.exports = { { query, access }: any ): Promise => { const namespace = context.req.user.namespace; + await enforceCheckForNoPendingRequests( + context, + namespace, + consumerId + ); try { logger.debug( '[grantAccessToConsumer] %s %s %j', @@ -265,6 +271,11 @@ module.exports = { { query, access }: any ): Promise => { const namespace = context.req.user.namespace; + await enforceCheckForNoPendingRequests( + context, + namespace, + consumerId + ); try { logger.debug( '[revokeAccessFromConsumer] %s %s %j', @@ -327,6 +338,11 @@ module.exports = { { query, access }: any ): Promise => { const namespace = context.req.user.namespace; + await enforceCheckForNoPendingRequests( + context, + namespace, + consumerId + ); try { logger.debug( '[updateConsumerAccess] %s %s %j', @@ -365,6 +381,11 @@ module.exports = { { query, access }: any ): Promise => { const namespace = context.req.user.namespace; + await enforceCheckForNoPendingRequests( + context, + namespace, + consumerId + ); await saveConsumerLabels(context, namespace, consumerId, labels); return true; }, diff --git a/src/lists/extensions/Namespace.ts b/src/lists/extensions/Namespace.ts index 59c815c5c..c680dd23b 100644 --- a/src/lists/extensions/Namespace.ts +++ b/src/lists/extensions/Namespace.ts @@ -241,7 +241,7 @@ module.exports = { info: any, { query, access }: any ) => { - const namespaceValidationRule = '^[a-z][a-z0-9-]{4,14}$'; + const namespaceValidationRule = '^[a-z][a-z0-9-]{3,13}[a-z0-9]$'; const re = new RegExp(namespaceValidationRule); assert.strictEqual( re.test(args.namespace), @@ -427,7 +427,7 @@ module.exports = { info: any, { query, access }: any ) => { - const namespaceValidationRule = '^[a-z][a-z0-9-]{4,14}$'; + const namespaceValidationRule = '^[a-z][a-z0-9-]{3,13}[a-z0-9]$'; const newNS = args.name ? args.name : newNamespaceID(); @@ -575,18 +575,18 @@ module.exports = { ); assert.strictEqual(nsResource.length, 1, 'Invalid Namespace'); - if (args.force === false) { - await DeleteNamespaceValidate( - context.createContext({ skipAccessControl: true }), - args.namespace - ); - } + await DeleteNamespaceValidate( + context.createContext({ skipAccessControl: true }), + args.namespace, + args.force + ); + await DeleteNamespace( context.sudo(), getSubjectToken(context.req), args.namespace ); - resourcesApi.deleteResourceSet(nsResource[0].id); + await resourcesApi.deleteResourceSet(nsResource[0].id); // Last thing to do is mark the Namespace group 'decommissioned' const nsService = new NamespaceService( diff --git a/src/nextapp/components/api-product-item/api-product-item.tsx b/src/nextapp/components/api-product-item/api-product-item.tsx index 72a4c8c73..e97879ecc 100644 --- a/src/nextapp/components/api-product-item/api-product-item.tsx +++ b/src/nextapp/components/api-product-item/api-product-item.tsx @@ -38,7 +38,7 @@ const ApiProductItem: React.FC = ({ id, preview, }) => { - const isPublic = data.environments.some((e) => e.flow === 'public'); + const isProtected = data.environments.some((e) => e.flow !== 'public'); const isTiered = data.environments.some((e) => e.anonymous); return ( @@ -49,7 +49,7 @@ const ApiProductItem: React.FC = ({ @@ -63,25 +63,13 @@ const ApiProductItem: React.FC = ({ )} - {!isTiered && ( - <> - {isPublic && ( - - )} - {!isPublic && ( - - )} - + {!isTiered && isProtected && ( + )} {isTiered && ( diff --git a/src/nextapp/components/authorization-profile-form/authorization-form.tsx b/src/nextapp/components/authorization-profile-form/authorization-form.tsx index 3e5bc0088..76f86fc84 100644 --- a/src/nextapp/components/authorization-profile-form/authorization-form.tsx +++ b/src/nextapp/components/authorization-profile-form/authorization-form.tsx @@ -99,11 +99,11 @@ const AuthorizationForm: React.FC = ({ description: `Automatic issuing of the credential means that this owner (${ownerName}) has configured appropriate credentials here to allow the API Manager to manage Clients on the particular OIDC Provider.`, value: 'auto', }, - { - title: 'Manual', - description: `Manual issuing of the credential means that this owner (${ownerName}) will complete setup of the new credential with the particular OIDC Provider, and communicate that to the requestor via email or other means.`, - value: 'manual', - }, + // { + // title: 'Manual', + // description: `Manual issuing of the credential means that this owner (${ownerName}) will complete setup of the new credential with the particular OIDC Provider, and communicate that to the requestor via email or other means.`, + // value: 'manual', + // }, ]} /> diff --git a/src/nextapp/components/manage-labels/manage-labels.tsx b/src/nextapp/components/manage-labels/manage-labels.tsx index e4537627d..3ea6ae004 100644 --- a/src/nextapp/components/manage-labels/manage-labels.tsx +++ b/src/nextapp/components/manage-labels/manage-labels.tsx @@ -133,7 +133,7 @@ const ManageLabels: React.FC = ({ data, id, queryKey }) => { } catch (err) { toast({ title: 'Labels Update Failed', - description: err.message, + description: err, status: 'error', }); } diff --git a/src/nextapp/components/service-routes/service-routes.tsx b/src/nextapp/components/service-routes/service-routes.tsx index 1a9680b96..3ea83b50b 100644 --- a/src/nextapp/components/service-routes/service-routes.tsx +++ b/src/nextapp/components/service-routes/service-routes.tsx @@ -25,6 +25,9 @@ const ServiceRoutes: React.FC = ({ data }) => { : ['ALL']; const hosts: string[] = JSON.parse(route.hosts); const paths: string[] = JSON.parse(route.paths) ?? ['/']; + if (paths.length === 0) { + paths.push('/'); + } const hostPaths = hosts .map((h: string) => paths.map((p) => `https://${h}${p}`)) .flat(); diff --git a/src/nextapp/pages/manager/authorization-profiles/index.tsx b/src/nextapp/pages/manager/authorization-profiles/index.tsx index 70bfc815d..f401af796 100644 --- a/src/nextapp/pages/manager/authorization-profiles/index.tsx +++ b/src/nextapp/pages/manager/authorization-profiles/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import api, { useApi, useApiMutation } from '@/shared/services/api'; import ActionsMenu from '@/components/actions-menu'; +import ConfirmationDialog from '@/components/confirmation-dialog'; import AuthorizationProfileForm from '@/components/authorization-profile-form'; import { Box, @@ -220,9 +221,15 @@ const AuthorizationProfiles: React.FC< - - Delete - + handleDelete(c.id))()} + > + Delete Profile... + diff --git a/src/nextapp/pages/manager/consumers/[id].tsx b/src/nextapp/pages/manager/consumers/[id].tsx index ce23590cd..adb2a1e93 100644 --- a/src/nextapp/pages/manager/consumers/[id].tsx +++ b/src/nextapp/pages/manager/consumers/[id].tsx @@ -110,7 +110,7 @@ const ConsumerPage: React.FC< status: 'error', description: Array.isArray(err) ? err.map((e) => e.message).join(', ') - : undefined, + : err, }); } }; diff --git a/src/nextapp/pages/manager/consumers/index.tsx b/src/nextapp/pages/manager/consumers/index.tsx index 5a05edd48..187168192 100644 --- a/src/nextapp/pages/manager/consumers/index.tsx +++ b/src/nextapp/pages/manager/consumers/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import ActionsMenu from '@/components/actions-menu'; +import ConfirmationDialog from '@/components/confirmation-dialog'; import api, { useApi, useApiMutation } from '@/shared/services/api'; import { Box, @@ -168,7 +169,7 @@ const ConsumersPage: React.FC< title: 'Consumer delete failed', description: Array.isArray(err) ? err.map((e) => e.message).join(', ') - : '', + : err, status: 'error', }); } @@ -293,13 +294,20 @@ const ConsumersPage: React.FC< > Grant Access - handleDelete(d.id))()} > - Delete Consumer - + + Delete Consumer... + + diff --git a/src/nextapp/pages/manager/namespaces/index.tsx b/src/nextapp/pages/manager/namespaces/index.tsx index 0a05dffbe..991053adf 100644 --- a/src/nextapp/pages/manager/namespaces/index.tsx +++ b/src/nextapp/pages/manager/namespaces/index.tsx @@ -157,6 +157,7 @@ const NamespacesPage: React.FC = () => { } catch (err) { toast({ title: 'Delete namespace failed', + description: err, status: 'error', isClosable: true, }); diff --git a/src/nextapp/pages/manager/products/index.tsx b/src/nextapp/pages/manager/products/index.tsx index f17978ca4..762042ba2 100644 --- a/src/nextapp/pages/manager/products/index.tsx +++ b/src/nextapp/pages/manager/products/index.tsx @@ -53,7 +53,7 @@ const ProductsPage: React.FC = () => { } catch (err) { toast({ title: 'Publish settings change failed', - description: `${err}`, + description: err, status: 'error', }); } @@ -104,11 +104,12 @@ const ProductsPage: React.FC = () => { size="sm" lineHeight="24px" > - Publish APIs + Grant permission to publish APIs - By enabling Publish APIs, consumers can find and request - access to your APIs from the Directory. + Allow providers to publish their products to the API + directory so they can make their APIs discoverable to + consumers. diff --git a/src/nextapp/pages/manager/service-accounts/index.tsx b/src/nextapp/pages/manager/service-accounts/index.tsx index 5e8fb4951..8801c4447 100644 --- a/src/nextapp/pages/manager/service-accounts/index.tsx +++ b/src/nextapp/pages/manager/service-accounts/index.tsx @@ -100,10 +100,7 @@ const ServiceAccountsPage: React.FC< title="Service Accounts" > - - Service Accounts allow you to access BC Government APIs via the - Gateway API or the Gateway CLI - + Service Accounts allow you to administer your gateway ( ); }; +async function prepareError(response: any) { + const contentType = response.headers.get('Content-Type'); + if (contentType?.includes('application/json')) { + const json = await response.json(); + const hasErrors = Boolean(json?.errors) || Boolean(json?.details); + if (hasErrors) { + if (json.errors && json.errors[0]?.data?.messages) { + throw json.errors[0]?.data?.messages.join('\n'); + } else if (json.details) { + const result = []; + Object.entries(json.details).forEach(([key, value]) => { + result.push(`${key}: ${(value as any).message}`); + }); + throw result.join('\n'); + } + throw json.errors?.map((e) => e.message).join('\n'); + } + } + throw `${response.statusText}`; +} + export default api; diff --git a/src/server.ts b/src/server.ts index 5ad25937c..749847e3e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -281,6 +281,15 @@ const configureExpress = (app: any) => { const express = require('express'); app.use(express.json()); + app.use(function errorHandler(err: any, req: any, res: any, next: any) { + if (err instanceof SyntaxError) { + return res.status(422).json({ + message: 'Syntax Error Parsing JSON', + }); + } + next(); + }); + // app.get('/', (req, res, next) => { // console.log(req.path) // req.path == "/" ? res.redirect('/home') : next() diff --git a/src/services/checkStatus.ts b/src/services/checkStatus.ts index 99b1acf80..f83b44500 100644 --- a/src/services/checkStatus.ts +++ b/src/services/checkStatus.ts @@ -1,13 +1,17 @@ import { logger } from '../logger'; -import { IssuerMisconfigError } from './issuerMisconfigError'; +import { + IssuerMisconfigDetail, + IssuerMisconfigError, +} from './issuerMisconfigError'; export async function checkStatus(res: any) { if (res.ok) { return res; } else { - const error = { + const error: IssuerMisconfigDetail = { reason: 'unknown_error', + description: '', status: `${res.status} ${res.statusText}`, }; logger.error('Error - %d %s', res.status, res.statusText); @@ -16,7 +20,8 @@ export async function checkStatus(res: any) { logger.error('ERROR ' + body); try { const errors = JSON.parse(body); - error['reason'] = errors['error']; + error.reason = errors?.error ?? ''; + error.description = errors?.error_description ?? ''; logger.error('Added reason to error: %j', error); } catch (e) { logger.error('Not able to parse error response (%s)', e); diff --git a/src/services/issuerMisconfigError.ts b/src/services/issuerMisconfigError.ts index 35273d76f..68510d3df 100644 --- a/src/services/issuerMisconfigError.ts +++ b/src/services/issuerMisconfigError.ts @@ -1,11 +1,22 @@ +export interface IssuerMisconfigDetail { + reason: string; + description: string; + status: string; +} + export class IssuerMisconfigError extends Error { - public errors: any; + public errors: IssuerMisconfigDetail[]; - constructor(message: any) { + constructor(message: IssuerMisconfigDetail) { super(JSON.stringify(message)); Error.captureStackTrace(this, this.constructor); this.name = this.constructor.name; this.errors = [message]; } + + text() { + const detail = this.errors[0]; + return `[status] "${detail.status}" [reason] "${detail.reason}" [description] "${detail.description}"`; + } } diff --git a/src/services/keystone/access-request.ts b/src/services/keystone/access-request.ts index b298c504d..289bbd2c0 100644 --- a/src/services/keystone/access-request.ts +++ b/src/services/keystone/access-request.ts @@ -121,6 +121,59 @@ export async function getAccessRequestByNamespaceServiceAccess( : result.data.allAccessRequests[0]; } +export async function getOpenAccessRequestsByConsumer( + context: any, + ns: string, + consumerId: string +): Promise { + logger.debug( + '[getOpenAccessRequestsByConsumer] ns=%s consumer=%s', + ns, + consumerId + ); + const query = gql` + query GetNamespaceOpenAccessRequestsByConsumer( + $ns: String! + $consumerId: String! + ) { + allAccessRequests( + where: { + serviceAccess: { consumer: { id: $consumerId } } + isComplete: false + } + ) { + id + name + isApproved + isIssued + isComplete + additionalDetails + serviceAccess { + id + consumer { + username + } + } + createdAt + } + } + `; + + const result = await context.executeGraphQL({ + query, + variables: { ns, consumerId }, + }); + logger.debug('[getOpenAccessRequestsByConsumer] result %j', result); + + assert.strictEqual( + 'errors' in result, + false, + 'Error retrieving access request record' + ); + + return result.data.allAccessRequests; +} + export async function lookupEnvironmentAndApplicationByAccessRequest( context: any, id: string diff --git a/src/services/keystone/index.ts b/src/services/keystone/index.ts index 84558efab..5be38d522 100644 --- a/src/services/keystone/index.ts +++ b/src/services/keystone/index.ts @@ -1,5 +1,6 @@ export { getAccessRequestsByNamespace, + getOpenAccessRequestsByConsumer, lookupEnvironmentAndApplicationByAccessRequest, linkServiceAccessToRequest, markAccessRequestAsNotIssued, diff --git a/src/services/notification/notification.service.ts b/src/services/notification/notification.service.ts index 5322c654d..a38cd3f28 100644 --- a/src/services/notification/notification.service.ts +++ b/src/services/notification/notification.service.ts @@ -9,6 +9,8 @@ import { NotificationConfig, EmailNotification, User } from './config'; import { ConfigService } from '../config.service'; +const isEmpty = (str: string) => !str?.length; + export class NotificationService { private notifyConfig: NotificationConfig; constructor(private readonly config: ConfigService) { @@ -16,23 +18,24 @@ export class NotificationService { } private templateToContent(to: User, templateName: string) { + const { logger } = this; + const name = isEmpty(to.name) ? 'Portal User' : to.name; + + logger.debug('Notification triggered [%s] %j', name, to); const template = fs.readFileSync( path.resolve(__dirname, `templates/${templateName}.html`), 'utf8' ); - return template.replace('{{name}}', to.name); + return template.replace('{{name}}', name); } public async notify(user: User, email: EmailNotification) { const { logger } = this; - try { if (!this.notifyConfig.enabled) { return; } - logger.debug('Notification triggered', user); - //we don't notify the active user at all as they made the change var emailContent = this.templateToContent(user, email.template); @@ -78,7 +81,7 @@ export class NotificationService { // this.logger.debug("Email sent: " + info.response); // }) } catch (err) { - logger.error('Sending notification to %s failed - %s', user.email, err); + logger.error('[FAILED] Notification to %s failed - %s', user.email, err); } } diff --git a/src/services/workflow/apply.ts b/src/services/workflow/apply.ts index 98aba907a..752ef9720 100644 --- a/src/services/workflow/apply.ts +++ b/src/services/workflow/apply.ts @@ -32,6 +32,7 @@ import { saveConsumerLabels } from './consumer-management'; import { StructuredActivityService } from './namespace-activity'; import { KeycloakClientRolesService } from '../keycloak/client-roles'; import { genClientId } from './client-shared-idp'; +import { IssuerMisconfigError } from '../issuerMisconfigError'; const logger = Logger('wf.Apply'); @@ -136,17 +137,30 @@ export const Apply = async ( logger.error('Failed to rollback access request %s', err); } ); - await recordActivity( - context, - operation, - 'AccessRequest', - updatedItem.id, - 'Failed to Apply Workflow - ' + err, - 'failed', - '', - productNamespace - ); - throw err; + if (err instanceof IssuerMisconfigError) { + await recordActivity( + context, + operation, + 'AccessRequest', + updatedItem.id, + `Failed to Apply Workflow: ${err.text()}`, + 'failed', + '', + productNamespace + ); + } else { + await recordActivity( + context, + operation, + 'AccessRequest', + updatedItem.id, + 'Failed to Apply Workflow - ' + err, + 'failed', + '', + productNamespace + ); + } + throw new Error('Communication error with issuer'); } return; } diff --git a/src/services/workflow/consumer-management.ts b/src/services/workflow/consumer-management.ts index ac49d6a78..fe3fc55d4 100644 --- a/src/services/workflow/consumer-management.ts +++ b/src/services/workflow/consumer-management.ts @@ -51,8 +51,8 @@ import { getAccessRequestByNamespaceServiceAccess, + getOpenAccessRequestsByConsumer, lookupConsumerPlugins, - lookupCredentialReferenceByServiceAccess, lookupLabeledServiceAccessesForNamespace, lookupServiceAccessesByConsumer, lookupEnvironmentAndIssuerById, @@ -80,12 +80,10 @@ import { GatewayConsumer, LabelCreateInput, LabelUpdateInput, - Product, } from '../keystone/types'; import { KeycloakClientRegistrationService, KeycloakClientService, - KeycloakUserService, } from '../keycloak'; import { addConsumerLabel, @@ -94,7 +92,6 @@ import { } from '../keystone/labels'; import { getActivityByRefId } from '../keystone/activity'; import { syncPlugins, trimPlugin } from './consumer-plugins'; -import { removeAllButKeys } from '../../batch/feed-worker'; import { KeycloakClientRolesService } from '../keycloak/client-roles'; import { genClientId } from './client-shared-idp'; @@ -148,8 +145,12 @@ export async function allScopesAndRoles( envs .filter((env) => env.credentialIssuer) .forEach((env) => { - result.scopes.push(...JSON.parse(env.credentialIssuer.availableScopes)); - result.roles.push(...JSON.parse(env.credentialIssuer.clientRoles)); + result.scopes.push( + ...JSON.parse(env.credentialIssuer.availableScopes ?? '[]') + ); + result.roles.push( + ...JSON.parse(env.credentialIssuer.clientRoles ?? '[]') + ); }); result.scopes = [...new Set(result.scopes)]; @@ -179,7 +180,11 @@ export async function getFilteredNamespaceConsumers( ); return accesses - .filter((acc) => acc.consumer) + .filter( + (acc, index, self) => + acc.consumer && + index === self.findIndex((t) => t.consumer.id === acc.consumer.id) + ) .map((acc) => { return { id: acc.consumer.id, @@ -519,6 +524,12 @@ export async function revokeAccessFromConsumer( const prodEnvAccessItem = prodEnvAccessFiltered[0]; + assert.strictEqual( + prodEnvAccessItem.serviceAccessId == null, + true, + 'Delete this consumer to revoke remaining access' + ); + assert.strictEqual(prodEnvAccessItem.revocable, true, 'Access not revocable'); const serviceAccessId = prodEnvAccessItem.serviceAccessId; @@ -744,7 +755,7 @@ export async function revokeAllConsumerAccess( assert.strictEqual( prodEnvAccess.length == 1 && prodEnvAccess[0].serviceAccessId != null, true, - 'Not eligible for deletion' + 'Not eligible for deletion: Revoke access to product environments before deletion' ); const serviceAccessId = prodEnvAccess[0].serviceAccessId; @@ -848,6 +859,23 @@ export async function saveConsumerLabels( logger.debug('[saveConsumerLabels] Changes %j', changes); } +export async function enforceCheckForNoPendingRequests( + context: any, + ns: string, + consumerId: string +) { + const openRequests = await getOpenAccessRequestsByConsumer( + context, + ns, + consumerId + ); + assert.strictEqual( + openRequests.length == 0, + true, + 'Pending access requests exist for this consumer; requests must be approved or rejected first' + ); +} + /** * * @param prodEnv diff --git a/src/services/workflow/delete-access.ts b/src/services/workflow/delete-access.ts index b772f6292..7c4f245d5 100644 --- a/src/services/workflow/delete-access.ts +++ b/src/services/workflow/delete-access.ts @@ -135,11 +135,11 @@ export const DeleteAccess = async (context: any, keys: any) => { svc.consumerType == 'client' && svc.consumer.username != 'anonymous' && svc.productEnvironment && - deleteRecord(context, 'GatewayConsumer', { id: svc.consumer.id }, [ + (await deleteRecord(context, 'GatewayConsumer', { id: svc.consumer.id }, [ 'id', 'extForeignKey', 'customId', - ]); + ])); if ( svc.consumer != null && @@ -147,8 +147,8 @@ export const DeleteAccess = async (context: any, keys: any) => { svc.consumer.username != 'anonymous' && svc.productEnvironment ) { - // Asynchronously do the deletion of the backend IdP and Kong - kongApi + // Synchronously do the deletion of the backend IdP and Kong + await kongApi .deleteConsumer(svc.consumer.extForeignKey) .then(async () => { if (flow == 'client-credentials') { diff --git a/src/services/workflow/delete-namespace.ts b/src/services/workflow/delete-namespace.ts index 0d63d9704..4abd6063a 100644 --- a/src/services/workflow/delete-namespace.ts +++ b/src/services/workflow/delete-namespace.ts @@ -33,7 +33,8 @@ const logger = Logger('wf.DeleteNamespace'); export const DeleteNamespaceValidate = async ( context: any, - ns: string + ns: string, + force: boolean ): Promise => { logger.debug('Validate Deleting Namespace ns=%s', ns); @@ -45,11 +46,6 @@ export const DeleteNamespaceValidate = async ( const accessList = await lookupServiceAccessesByEnvironment(context, ns, ids); - const serviceAccountAccessList = await lookupServiceAccessesForNamespace( - context, - ns - ); - const messages = []; if (accessList.length > 0) { messages.push( @@ -65,21 +61,17 @@ export const DeleteNamespaceValidate = async ( } been configured in this namespace.` ); } - if (serviceAccountAccessList.length > 0) { - messages.push( - `${serviceAccountAccessList.length} ${ - serviceAccountAccessList.length == 1 - ? 'service account exists' - : 'service accounts exist' - } in this namespace.` - ); - } - assert.strictEqual( - gwServices.length == 0 && accessList.length == 0, - true, - messages.join(' ') - ); + // Even when force=true, if there are consumers then do not proceed + if (accessList.length != 0) { + const msg = `${accessList.length} ${ + accessList.length == 1 ? 'consumer has' : 'consumers have' + } access to products in this namespace.`; + assert.strictEqual(accessList.length == 0, true, msg); + } + if (!force) { + assert.strictEqual(gwServices.length == 0, true, messages.join(' ')); + } }; export const DeleteNamespaceRecordActivity = async ( diff --git a/src/services/workflow/index.ts b/src/services/workflow/index.ts index bb5b8627b..c6f57aa2d 100644 --- a/src/services/workflow/index.ts +++ b/src/services/workflow/index.ts @@ -18,6 +18,7 @@ export { revokeAllConsumerAccess, revokeAccessFromConsumer, updateConsumerAccess, + enforceCheckForNoPendingRequests, saveConsumerLabels, } from './consumer-management'; diff --git a/src/test/integrated/keystonejs/accessRequest.ts b/src/test/integrated/keystonejs/accessRequest.ts new file mode 100644 index 000000000..0ecade6ae --- /dev/null +++ b/src/test/integrated/keystonejs/accessRequest.ts @@ -0,0 +1,43 @@ +/* +Wire up directly with Keycloak and use the Services +To run: +npm run ts-build +npm run ts-watch +node dist/test/integrated/keystonejs/accessRequest.js +*/ + +import InitKeystone from './init'; +import { o } from '../util'; +import { getOpenAccessRequestsByConsumer } from '../../../services/keystone/access-request'; + +(async () => { + const keystone = await InitKeystone(); + + const ns = 'gw-0dcd7'; + const skipAccessControl = false; + + const identity = { + id: null, + username: 'sample_username', + namespace: ns, + roles: JSON.stringify(['api-owner']), + scopes: [], + userId: null, + } as any; + + const ctx = keystone.createContext({ + skipAccessControl, + authentication: { item: identity }, + }); + + // o(await getOrganizations(ctx)); + + const serviceAccess = await getOpenAccessRequestsByConsumer( + ctx, + ns, + '653860ee26683257394cfe3c' + ); + o(serviceAccess); + + await keystone.disconnect(); +})(); diff --git a/src/test/mock-server/ds-api.js b/src/test/mock-server/ds-api.js index 156267695..ba51956df 100644 --- a/src/test/mock-server/ds-api.js +++ b/src/test/mock-server/ds-api.js @@ -84,7 +84,7 @@ router.get('/documentation/:slug', async (_, res) => { tags: ['ns.platform'], title: 'API Owner User Journey', content: - '# API Owner Flow\n\nThe following steps walk an API Owner through setting up an API on the BC Gov API Gateway in our Test instance. If you are ready to deploy to our Production instance, use the links [here](#production-links).\n\n## 1. Register a new namespace\n\nA `namespace` represents a collection of Kong Services and Routes that are managed independently.\n\nTo create a new namespace, go to the [API Services Portal](https://gwa-apps-gov-bc-ca.test.api.gov.bc.ca/int).\n\nAfter login (and selection of an existing namespace if you have one already assigned), go to the `New Namespace` tab and click the `Create Namespace` button.\n\nThe namespace must be an alphanumeric string between 5 and 15 characters (RegExp reference: `^[a-z][a-z0-9-]{4,14}$`).\n\nLogout by clicking your username at the top right of the page. When you login again, you should be able to select the new namespace from the `API Programme Services` project selector.\n\n## 2. Generate a Service Account\n\nGo to the `Service Accounts` tab and click the `Create Service Account`. A new credential will be created - make a note of the `ID` and `Secret`.\n\nThe credential has the following access:\n* `Gateway.Write` : Permission to publish gateway configuration to Kong\n* `Access.Write` : Permission to update the Access Control List for controlling access to viewing metrics, service configuration and service account management\n* `Catalog.Write` : Permission to update BC Data Catalog datasets for describing APIs available for consumption\n\n## 3. Prepare configuration\n\nThe gateway configuration can be hand-crafted or you can use a command line interface that we developed called `gwa` to convert your Openapi v3 spec to a Kong configuration.\n\n### 3.1. Hand-crafted (recommended if you don\'t have an Openapi spec)\n\n**Simple Example**\n\n``` bash\nexport NS="my_namespace"\nexport NAME="a-service-for-$NS"\necho "\nservices:\n- name: $NAME\n host: httpbin.org\n tags: [ ns.$NS ]\n port: 443\n protocol: https\n retries: 0\n routes:\n - name: $NAME-route\n tags: [ ns.$NS ]\n hosts:\n - $NAME.api.gov.bc.ca\n paths:\n - /\n strip_path: false\n https_redirect_status_code: 426\n path_handling: v0\n" > sample.yaml\n```\n\n> To view common plugin config go to [COMMON-CONFIG.md](/docs/COMMON-CONFIG.md)\n\n> To view some other plugin examples go [here](/docs/samples/service-plugins).\n\n> **Declarative Config** Behind the scenes, DecK is used to sync your configuration with Kong. For reference: https://docs.konghq.com/deck/overview/\n\n> **Splitting Your Config:** A namespace `tag` with the format `ns.$NS` is mandatory for each service/route/plugin. But if you have separate pipelines for your environments (i.e./ dev, test and prod), you can split your configuration and update the `tags` with the qualifier. So for example, you can use a tag `ns.$NS.dev` to sync Kong configuration for `dev` Service and Routes only.\n\n> **Upstream Services on OCP4:** If your service is running on OCP4, you should specify the Kubernetes Service in the `Service.host`. It must have the format: `..svc`. Also make sure your `Service.port` matches your Kubernetes Service Port. Any Security Policies for egress from the Gateway will be setup automatically on the API Gateway side.\n> The Aporeto Network Security Policies are being removed in favor of the Kubernetes Security Policies (KSP). You will need to create a KSP on your side looking something like this to allow the Gateway\'s test and prod environments to route traffic to your API:\n\n``` yaml\nkind: NetworkPolicy\napiVersion: networking.k8s.io/v1\nmetadata:\n name: allow-traffic-from-gateway-to-your-api\nspec:\n podSelector:\n matchLabels:\n name: my-upstream-api\n ingress:\n - from:\n - namespaceSelector:\n matchLabels:\n environment: test\n name: 264e6f\n - from:\n - namespaceSelector:\n matchLabels:\n environment: prod\n name: 264e6f\n```\n\n> **Migrating from OCP3 to OCP4?** Please review the [OCP4-Migration](docs/OCP4-MIGRATION.md) instructions to help with transitioning to OCP4 and the new APS Gateway.\n\n> **Require mTLS between the Gateway and your Upstream Service?** To support mTLS on your Upstream Service, you will need to provide client certificate details and if you want to verify the upstream endpoint then the `ca_certificates` and `tls_verify` is required as well. An example:\n\n``` yaml\nservices:\n- name: my-upstream-service\n host: my-upstream.site\n tags: [ _NS_ ]\n port: 443\n protocol: https\n tls_verify: true\n ca_certificates: [ 0a780ee0-626c-11eb-ae93-0242ac130012 ]\n client_certificate: 8fc131ef-9752-43a4-ba70-eb10ba442d4e\n routes: [ ... ]\ncertificates:\n- cert: ""\n key: ""\n tags: [ _NS_ ]\n id: 8fc131ef-9752-43a4-ba70-eb10ba442d4e\nca_certificates:\n- cert: ""\n tags: [ _NS_ ]\n id: 0a780ee0-626c-11eb-ae93-0242ac130012\n```\n\n> NOTE: You must generate a UUID (`python -c \'import uuid; print(uuid.uuid4())\'`) for each certificate and ca_certificate you create (set the `id`) and reference it in your `services` details.\n\n> HELPER: Python command to get a PEM file on one line: `python -c \'import sys; import json; print(json.dumps(open(sys.argv[1]).read()))\' my.pem`\n\n### 3.2. gwa Command Line\n\nRun: `gwa new` and follow the prompts.\n\nExample:\n\n``` bash\ngwa new -o sample.yaml \\\n --route-host myapi.api.gov.bc.ca \\\n --service-url https://httpbin.org \\\n https://bcgov.github.io/gwa-api/openapi/simple.yaml\n```\n\n> See below for the `gwa` CLI install instructions.\n\n## 4. Apply gateway configuration\n\nThe Swagger console for the `gwa-api` can be used to publish Kong Gateway configuration, or the `gwa Command Line` can be used.\n\n### 4.1. gwa Command Line (recommended)\n\n**Install (for Linux)**\n\n``` bash\nGWA_CLI_VERSION=v1.1.3; curl -L -O https://github.com/bcgov/gwa-cli/releases/download/${GWA_CLI_VERSION}/gwa_${GWA_CLI_VERSION}_linux_x64.zip\nunzip gwa_${GWA_CLI_VERSION}_linux_x64.zip\n./gwa --version\n```\n\n> **Using MacOS or Windows?** Download here: https://github.com/bcgov/gwa-cli/releases/tag/v1.1.3\n\n**Configure**\n\nCreate a `.env` file and update the CLIENT_ID and CLIENT_SECRET with the new credentials that were generated in step #2:\n\n``` bash\necho "\nGWA_NAMESPACE=$NS\nCLIENT_ID=\nCLIENT_SECRET=\nGWA_ENV=test\n" > .env\n\nOR run:\n\ngwa init -T --namespace=$NS --client-id= --client-secret=\n\n```\n\n> NOTE: The `-T` indicates our Test environment. For production use `-P`.\n\n**Publish**\n\n``` bash\ngwa pg sample.yaml \n```\n\nIf you want to see the expected changes but not actually apply them, you can run:\n\n``` bash\ngwa pg --dry-run sample.yaml\n```\n\n### 4.2. Swagger Console\n\nGo to gwa-api Swagger Console.\n\nSelect the `PUT` `/namespaces/{namespace}/gateway` API.\n\nThe Service Account uses the OAuth2 Client Credentials Grant Flow. Click the `lock` link on the right and enter in the Service Account credentials that were generated in step #2.\n\nFor the `Parameter namespace`, enter the namespace that you created in step #1.\n\nSelect `dryRun` to `true`.\n\nSelect a `configFile` file.\n\nSend the request.\n\n### 4.3. Postman\n\nFrom the Postman App, click the `Import` button and go to the `Link` tab.\n\nEnter a URL: https://openapi-to-postman-api-gov-bc-ca.test.api.gov.bc.ca/?url=https://gwa-api-gov-bc-ca.test.api.gov.bc.ca/api/doc/swagger.json\n\nAfter creation, go to `Collections` and right-click on the `Gateway Administration (GWA) API` collection and select `edit`.\n\nGo to the `Authorization` tab, enter in your `Client ID` and `Client Secret` and click `Get New Access Token`.\n\nYou should get a successful dialog to proceed. Click `Proceed` and `Use Token`.\n\nYou can then verify that the token works by going to the Collection `Return key information about authenticated identity` and click `Send`.\n\n## 5. Verify routes\n\nTo verify that the Gateway can access the upstream services, run the command: `gwa status`.\n\nIn our test environment, the hosts that you defined in the routes get altered; to see the actual hosts, log into the API Services Portal and view the hosts under `Services`.\n\n``` bash\ncurl https://${NAME}-api-gov-bc-ca.test.api.gov.bc.ca/headers\n\nab -n 20 -c 2 https://${NAME}-api-gov-bc-ca.test.api.gov.bc.ca/headers\n\n```\n\nTo help with troubleshooting, you can use the GWA API to get a health check for each of the upstream services to verify the Gateway is connecting OK.\n\nGo to the GWA API, enter in the new credentials that were generated in step #2, click `Try it out`, enter your namespace and click `Execute`. The results are returned in a JSON object.\n\n## 6. View metrics\n\nThe following metrics can be viewed in real-time for the Services that you configure on the Gateway:\n\n* Request Rate : Requests / Second (by Service/Route, by HTTP Status)\n* Latency : Standard deviations measured for latency inside Kong and on the Upstream Service (by Service/Route)\n* Bandwidth : Ingress/egress bandwidth (by Service/Route)\n* Total Requests : In 5 minute windows (by Consumer, by User Agent, by Service, by HTTP Status)\n\nAll metrics can be viewed by an arbitrary time window - defaults to `Last 24 Hours`.\n\nGo to Grafana to view metrics for your configured services.\n\nYou can also access the metrics from the `API Services Portal`.\n\n## 7. Grant access to others\n\nThe `acl` command provides a way to update the access for the namespace. It expects an all-inclusive membership list, so if a user is not either part of the `--users` list or the `--managers` list, they will be removed from the namespace.\n\nFor elevated privileges (such as managing Service Accounts), add the usernames to the `--managers` argument.\n\n``` bash\ngwa acl --users jjones@idir --managers acope@idir\n```\n\nThe result will show the ACL changes. The Add/Delete counts represent the membership changes of registered users. The Missing count represents the users that will automatically be added to the namespace once they have logged into the `APS Services Portal`.\n\n## 8. Add to your CI/CD Pipeline\n\nUpdate your CI/CD pipelines to run the `gwa-cli` to keep your services updated on the gateway.\n\n### 8.1. Github Actions Example\n\nIn the repository that you maintain your CI/CD Pipeline configuration, use the Service Account details from `Step 2` to set up two `Secrets`:\n\n* GWA_ACCT_ID\n\n* GWA_ACCT_SECRET\n\nAdd a `.gwa` folder (can be called anything) that will be used to hold your gateway configuration.\n\nAn example Github Workflow:\n\n``` yaml\nenv:\n NS: ""\n \njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v2\n with:\n fetch-depth: 0\n \n - uses: actions/setup-node@v1\n with:\n node-version: 10\n TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Get GWA Command Line\n run: |\n curl -L -O https://github.com/bcgov/gwa-cli/releases/download/v1.1.3/gwa_v1.1.3_linux_x64.zip\n unzip gwa_v1.1.3_linux_x64.zip\n export PATH=`pwd`:$PATH\n\n - name: Apply Namespace Configuration\n run: |\n export PATH=`pwd`:$PATH\n cd .gwa/$NS\n\n gwa init -T \\\n --namespace=$NS \\\n --client-id=${{ secrets.TEST_GWA_ACCT_ID }} \\\n --client-secret=${{ secrets.TEST_GWA_ACCT_SECRET }}\n\n gwa pg\n\n gwa acl --managers acope@idir\n \n```\n\n## 9. Share your API for Discovery\n\nPackage your APIs and make them available for discovery through the API Portal and BC Data Catalog.\n\n**Coming soon!**\n\n# Production Links\n\n* API Services Portal\n* gwa-api Swagger Console\n* OpenAPI to Postman Converter: https://openapi-to-postman.api.gov.bc.ca/?u=https://gwa.api.gov.bc.ca/api/doc/swagger.json\n* APS Metrics - Grafana\n', + '# API Owner Flow\n\nThe following steps walk an API Owner through setting up an API on the BC Gov API Gateway in our Test instance. If you are ready to deploy to our Production instance, use the links [here](#production-links).\n\n## 1. Register a new namespace\n\nA `namespace` represents a collection of Kong Services and Routes that are managed independently.\n\nTo create a new namespace, go to the [API Services Portal](https://gwa-apps-gov-bc-ca.test.api.gov.bc.ca/int).\n\nAfter login (and selection of an existing namespace if you have one already assigned), go to the `New Namespace` tab and click the `Create Namespace` button.\n\nThe namespace must be an alphanumeric string between 5 and 15 characters (RegExp reference: `^[a-z][a-z0-9-]{3,13}[a-z0-9]$`).\n\nLogout by clicking your username at the top right of the page. When you login again, you should be able to select the new namespace from the `API Programme Services` project selector.\n\n## 2. Generate a Service Account\n\nGo to the `Service Accounts` tab and click the `Create Service Account`. A new credential will be created - make a note of the `ID` and `Secret`.\n\nThe credential has the following access:\n* `Gateway.Write` : Permission to publish gateway configuration to Kong\n* `Access.Write` : Permission to update the Access Control List for controlling access to viewing metrics, service configuration and service account management\n* `Catalog.Write` : Permission to update BC Data Catalog datasets for describing APIs available for consumption\n\n## 3. Prepare configuration\n\nThe gateway configuration can be hand-crafted or you can use a command line interface that we developed called `gwa` to convert your Openapi v3 spec to a Kong configuration.\n\n### 3.1. Hand-crafted (recommended if you don\'t have an Openapi spec)\n\n**Simple Example**\n\n``` bash\nexport NS="my_namespace"\nexport NAME="a-service-for-$NS"\necho "\nservices:\n- name: $NAME\n host: httpbin.org\n tags: [ ns.$NS ]\n port: 443\n protocol: https\n retries: 0\n routes:\n - name: $NAME-route\n tags: [ ns.$NS ]\n hosts:\n - $NAME.api.gov.bc.ca\n paths:\n - /\n strip_path: false\n https_redirect_status_code: 426\n path_handling: v0\n" > sample.yaml\n```\n\n> To view common plugin config go to [COMMON-CONFIG.md](/docs/COMMON-CONFIG.md)\n\n> To view some other plugin examples go [here](/docs/samples/service-plugins).\n\n> **Declarative Config** Behind the scenes, DecK is used to sync your configuration with Kong. For reference: https://docs.konghq.com/deck/overview/\n\n> **Splitting Your Config:** A namespace `tag` with the format `ns.$NS` is mandatory for each service/route/plugin. But if you have separate pipelines for your environments (i.e./ dev, test and prod), you can split your configuration and update the `tags` with the qualifier. So for example, you can use a tag `ns.$NS.dev` to sync Kong configuration for `dev` Service and Routes only.\n\n> **Upstream Services on OCP4:** If your service is running on OCP4, you should specify the Kubernetes Service in the `Service.host`. It must have the format: `..svc`. Also make sure your `Service.port` matches your Kubernetes Service Port. Any Security Policies for egress from the Gateway will be setup automatically on the API Gateway side.\n> The Aporeto Network Security Policies are being removed in favor of the Kubernetes Security Policies (KSP). You will need to create a KSP on your side looking something like this to allow the Gateway\'s test and prod environments to route traffic to your API:\n\n``` yaml\nkind: NetworkPolicy\napiVersion: networking.k8s.io/v1\nmetadata:\n name: allow-traffic-from-gateway-to-your-api\nspec:\n podSelector:\n matchLabels:\n name: my-upstream-api\n ingress:\n - from:\n - namespaceSelector:\n matchLabels:\n environment: test\n name: 264e6f\n - from:\n - namespaceSelector:\n matchLabels:\n environment: prod\n name: 264e6f\n```\n\n> **Migrating from OCP3 to OCP4?** Please review the [OCP4-Migration](docs/OCP4-MIGRATION.md) instructions to help with transitioning to OCP4 and the new APS Gateway.\n\n> **Require mTLS between the Gateway and your Upstream Service?** To support mTLS on your Upstream Service, you will need to provide client certificate details and if you want to verify the upstream endpoint then the `ca_certificates` and `tls_verify` is required as well. An example:\n\n``` yaml\nservices:\n- name: my-upstream-service\n host: my-upstream.site\n tags: [ _NS_ ]\n port: 443\n protocol: https\n tls_verify: true\n ca_certificates: [ 0a780ee0-626c-11eb-ae93-0242ac130012 ]\n client_certificate: 8fc131ef-9752-43a4-ba70-eb10ba442d4e\n routes: [ ... ]\ncertificates:\n- cert: ""\n key: ""\n tags: [ _NS_ ]\n id: 8fc131ef-9752-43a4-ba70-eb10ba442d4e\nca_certificates:\n- cert: ""\n tags: [ _NS_ ]\n id: 0a780ee0-626c-11eb-ae93-0242ac130012\n```\n\n> NOTE: You must generate a UUID (`python -c \'import uuid; print(uuid.uuid4())\'`) for each certificate and ca_certificate you create (set the `id`) and reference it in your `services` details.\n\n> HELPER: Python command to get a PEM file on one line: `python -c \'import sys; import json; print(json.dumps(open(sys.argv[1]).read()))\' my.pem`\n\n### 3.2. gwa Command Line\n\nRun: `gwa new` and follow the prompts.\n\nExample:\n\n``` bash\ngwa new -o sample.yaml \\\n --route-host myapi.api.gov.bc.ca \\\n --service-url https://httpbin.org \\\n https://bcgov.github.io/gwa-api/openapi/simple.yaml\n```\n\n> See below for the `gwa` CLI install instructions.\n\n## 4. Apply gateway configuration\n\nThe Swagger console for the `gwa-api` can be used to publish Kong Gateway configuration, or the `gwa Command Line` can be used.\n\n### 4.1. gwa Command Line (recommended)\n\n**Install (for Linux)**\n\n``` bash\nGWA_CLI_VERSION=v1.1.3; curl -L -O https://github.com/bcgov/gwa-cli/releases/download/${GWA_CLI_VERSION}/gwa_${GWA_CLI_VERSION}_linux_x64.zip\nunzip gwa_${GWA_CLI_VERSION}_linux_x64.zip\n./gwa --version\n```\n\n> **Using MacOS or Windows?** Download here: https://github.com/bcgov/gwa-cli/releases/tag/v1.1.3\n\n**Configure**\n\nCreate a `.env` file and update the CLIENT_ID and CLIENT_SECRET with the new credentials that were generated in step #2:\n\n``` bash\necho "\nGWA_NAMESPACE=$NS\nCLIENT_ID=\nCLIENT_SECRET=\nGWA_ENV=test\n" > .env\n\nOR run:\n\ngwa init -T --namespace=$NS --client-id= --client-secret=\n\n```\n\n> NOTE: The `-T` indicates our Test environment. For production use `-P`.\n\n**Publish**\n\n``` bash\ngwa pg sample.yaml \n```\n\nIf you want to see the expected changes but not actually apply them, you can run:\n\n``` bash\ngwa pg --dry-run sample.yaml\n```\n\n### 4.2. Swagger Console\n\nGo to gwa-api Swagger Console.\n\nSelect the `PUT` `/namespaces/{namespace}/gateway` API.\n\nThe Service Account uses the OAuth2 Client Credentials Grant Flow. Click the `lock` link on the right and enter in the Service Account credentials that were generated in step #2.\n\nFor the `Parameter namespace`, enter the namespace that you created in step #1.\n\nSelect `dryRun` to `true`.\n\nSelect a `configFile` file.\n\nSend the request.\n\n### 4.3. Postman\n\nFrom the Postman App, click the `Import` button and go to the `Link` tab.\n\nEnter a URL: https://openapi-to-postman-api-gov-bc-ca.test.api.gov.bc.ca/?url=https://gwa-api-gov-bc-ca.test.api.gov.bc.ca/api/doc/swagger.json\n\nAfter creation, go to `Collections` and right-click on the `Gateway Administration (GWA) API` collection and select `edit`.\n\nGo to the `Authorization` tab, enter in your `Client ID` and `Client Secret` and click `Get New Access Token`.\n\nYou should get a successful dialog to proceed. Click `Proceed` and `Use Token`.\n\nYou can then verify that the token works by going to the Collection `Return key information about authenticated identity` and click `Send`.\n\n## 5. Verify routes\n\nTo verify that the Gateway can access the upstream services, run the command: `gwa status`.\n\nIn our test environment, the hosts that you defined in the routes get altered; to see the actual hosts, log into the API Services Portal and view the hosts under `Services`.\n\n``` bash\ncurl https://${NAME}-api-gov-bc-ca.test.api.gov.bc.ca/headers\n\nab -n 20 -c 2 https://${NAME}-api-gov-bc-ca.test.api.gov.bc.ca/headers\n\n```\n\nTo help with troubleshooting, you can use the GWA API to get a health check for each of the upstream services to verify the Gateway is connecting OK.\n\nGo to the GWA API, enter in the new credentials that were generated in step #2, click `Try it out`, enter your namespace and click `Execute`. The results are returned in a JSON object.\n\n## 6. View metrics\n\nThe following metrics can be viewed in real-time for the Services that you configure on the Gateway:\n\n* Request Rate : Requests / Second (by Service/Route, by HTTP Status)\n* Latency : Standard deviations measured for latency inside Kong and on the Upstream Service (by Service/Route)\n* Bandwidth : Ingress/egress bandwidth (by Service/Route)\n* Total Requests : In 5 minute windows (by Consumer, by User Agent, by Service, by HTTP Status)\n\nAll metrics can be viewed by an arbitrary time window - defaults to `Last 24 Hours`.\n\nGo to Grafana to view metrics for your configured services.\n\nYou can also access the metrics from the `API Services Portal`.\n\n## 7. Grant access to others\n\nThe `acl` command provides a way to update the access for the namespace. It expects an all-inclusive membership list, so if a user is not either part of the `--users` list or the `--managers` list, they will be removed from the namespace.\n\nFor elevated privileges (such as managing Service Accounts), add the usernames to the `--managers` argument.\n\n``` bash\ngwa acl --users jjones@idir --managers acope@idir\n```\n\nThe result will show the ACL changes. The Add/Delete counts represent the membership changes of registered users. The Missing count represents the users that will automatically be added to the namespace once they have logged into the `APS Services Portal`.\n\n## 8. Add to your CI/CD Pipeline\n\nUpdate your CI/CD pipelines to run the `gwa-cli` to keep your services updated on the gateway.\n\n### 8.1. Github Actions Example\n\nIn the repository that you maintain your CI/CD Pipeline configuration, use the Service Account details from `Step 2` to set up two `Secrets`:\n\n* GWA_ACCT_ID\n\n* GWA_ACCT_SECRET\n\nAdd a `.gwa` folder (can be called anything) that will be used to hold your gateway configuration.\n\nAn example Github Workflow:\n\n``` yaml\nenv:\n NS: ""\n \njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v2\n with:\n fetch-depth: 0\n \n - uses: actions/setup-node@v1\n with:\n node-version: 10\n TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Get GWA Command Line\n run: |\n curl -L -O https://github.com/bcgov/gwa-cli/releases/download/v1.1.3/gwa_v1.1.3_linux_x64.zip\n unzip gwa_v1.1.3_linux_x64.zip\n export PATH=`pwd`:$PATH\n\n - name: Apply Namespace Configuration\n run: |\n export PATH=`pwd`:$PATH\n cd .gwa/$NS\n\n gwa init -T \\\n --namespace=$NS \\\n --client-id=${{ secrets.TEST_GWA_ACCT_ID }} \\\n --client-secret=${{ secrets.TEST_GWA_ACCT_SECRET }}\n\n gwa pg\n\n gwa acl --managers acope@idir\n \n```\n\n## 9. Share your API for Discovery\n\nPackage your APIs and make them available for discovery through the API Portal and BC Data Catalog.\n\n**Coming soon!**\n\n# Production Links\n\n* API Services Portal\n* gwa-api Swagger Console\n* OpenAPI to Postman Converter: https://openapi-to-postman.api.gov.bc.ca/?u=https://gwa.api.gov.bc.ca/api/doc/swagger.json\n* APS Metrics - Grafana\n', readme: null, githubRepository: null, publishDate: '2021-05-27T12:00:00.000-08:00',