diff --git a/.github/workflows/aps-cypress-e2e.yaml b/.github/workflows/aps-cypress-e2e.yaml index f0ce8c604..9f1f7e170 100644 --- a/.github/workflows/aps-cypress-e2e.yaml +++ b/.github/workflows/aps-cypress-e2e.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Build GWA API Image run: | - git clone https://github.com/bcgov/gwa-api.git --branch v1.0.40 + git clone https://github.com/bcgov/gwa-api.git --branch v1.0.47 cd gwa-api/microservices/gatewayApi docker build -t gwa-api:e2e . diff --git a/.github/workflows/ci-feat-sonar.yaml b/.github/workflows/ci-feat-sonar.yaml index 851649d02..234e2e1a0 100644 --- a/.github/workflows/ci-feat-sonar.yaml +++ b/.github/workflows/ci-feat-sonar.yaml @@ -19,9 +19,13 @@ jobs: with: fetch-depth: 0 + - name: Install deps + run: | + sudo apt update + - uses: actions/setup-node@v2 with: - node-version: '14' + node-version: '22' - name: Run Tests run: | @@ -36,7 +40,7 @@ jobs: cd src - npm i + npm i --legacy-peer-deps npm run intg-build npm test @@ -45,13 +49,6 @@ jobs: - name: SonarCloud Scan uses: sonarsource/sonarqube-scan-action@master - with: - args: > - -Dsonar.organization=bcgov-oss - -Dsonar.projectKey=aps-portal - -Dsonar.host.url=https://sonarcloud.io - -Dsonar.sources=src/auth,src/authz,src/batch,src/services - -Dsonar.javascript.lcov.reportPaths=./src/__coverage__/lcov.info env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/README.md b/README.md index b43db75be..d40d6fcaf 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,12 @@ Use the following configuration to run the Portal locally (outside of Docker) ag 1. Start the OAuth2 Proxy locally: ```sh +# mac hostip=$(ifconfig en0 | awk '$1 == "inet" {print $2}') +# WSL +hostip=$(hostname -I | awk '{print $1}') + docker run -ti --rm --name proxy --net=host \ --add-host portal.localtest.me:$hostip \ -v `pwd`/local/oauth2-proxy/oauth2-proxy-dev.yaml:/oauth2.yaml \ diff --git a/docker-compose.yml b/docker-compose.yml index 9fea573e7..b39fe57c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - x-common-variables: &common-variables KONG_DATABASE: postgres KONG_PG_DATABASE: kong @@ -13,7 +11,8 @@ services: container_name: keycloak hostname: keycloak depends_on: - - kong-db + kong-db: + condition: service_healthy command: [ '-b', @@ -103,7 +102,7 @@ services: networks: - aps-net kong-db: - image: postgres:12.8 + image: postgres:14 container_name: kong-db environment: POSTGRES_USER: postgres @@ -115,13 +114,19 @@ services: volumes: - ./local/db/database-init.sql:/docker-entrypoint-initdb.d/1-init.sql - ./local/db/keystone-init.sql:/docker-entrypoint-initdb.d/2-init.sql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 10 networks: - aps-net kong-migrations: image: kong:kong-local command: kong migrations bootstrap depends_on: - - kong-db + kong-db: + condition: service_healthy environment: <<: *common-variables KONG_DNS_ORDER: 'LAST,A' @@ -135,8 +140,10 @@ services: image: kong:kong-local command: kong migrations up && kong migrations finish depends_on: - - kong-db - - kong-migrations + kong-db: + condition: service_healthy + kong-migrations: + condition: service_completed_successfully environment: <<: *common-variables KONG_DNS_ORDER: 'LAST,A' @@ -216,15 +223,15 @@ services: entrypoint: sh -c "chmod +x /tmp/entrypoint.sh && /tmp/entrypoint.sh" environment: - BROWSER=edge - - CYPRESS_RECORD_KEY=${CY_RECORD_KEY} - - CYPRESS_PROJECT_ID=${CY_PROJECT_ID} - - RUN_ENV=${CY_EXECUTION_ENV} - - COMMIT_INFO_BRANCH=${CY_COMMIT_BRANCH} - - COMMIT_INFO_SHA=${CY_COMMIT_SHA} - - COMMIT_INFO_AUTHOR=${CY_COMMIT_AUTHOR} - - COMMIT_INFO_MESSAGE=${CY_COMMIT_MESSAGE} - - COMMIT_INFO_REMOTE=${CY_REPO_URL} - - COMMIT_INFO_EMAIL=${CY_COMMIT_AUTHOR_EMAIL} + - CYPRESS_RECORD_KEY=${CY_RECORD_KEY:-} + - CYPRESS_PROJECT_ID=${CY_PROJECT_ID:-} + - RUN_ENV=${CY_EXECUTION_ENV:-} + - COMMIT_INFO_BRANCH=${CY_COMMIT_BRANCH:-} + - COMMIT_INFO_SHA=${CY_COMMIT_SHA:-} + - COMMIT_INFO_AUTHOR=${CY_COMMIT_AUTHOR:-} + - COMMIT_INFO_MESSAGE=${CY_COMMIT_MESSAGE:-} + - COMMIT_INFO_REMOTE=${CY_REPO_URL:-} + - COMMIT_INFO_EMAIL=${CY_COMMIT_AUTHOR_EMAIL:-} depends_on: - feeder-seeding build: diff --git a/e2e/Dockerfile b/e2e/Dockerfile index 74f6b878b..cb8708b7b 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -1,4 +1,4 @@ -FROM cypress/included:12.17.4 +FROM cypress/included:13.17.0 # Copy of the source for code coverage analysis WORKDIR /app @@ -22,7 +22,7 @@ COPY e2e/*.yml /e2e COPY e2e/entrypoint.sh /tmp ADD e2e/cypress /e2e/cypress -RUN curl -v -L -O https://github.com/bcgov/gwa-cli/releases/download/v3.0.5/gwa_Linux_x86_64.tgz \ +RUN curl -v -L -O https://github.com/bcgov/gwa-cli/releases/download/v3.0.6/gwa_Linux_x86_64.tgz \ && tar -xzf gwa_Linux_x86_64.tgz \ && mv gwa /usr/local/bin/. diff --git a/e2e/cypress.config.ts b/e2e/cypress.config.ts index 9e9a28add..0b12e2d9c 100644 --- a/e2e/cypress.config.ts +++ b/e2e/cypress.config.ts @@ -15,7 +15,10 @@ export default defineConfig({ // You may want to clean this up later by importing these. setupNodeEvents(on, config) { require('dotenv').config() - require('@cypress/code-coverage/task')(on, config) + // Only enable code coverage in CI or when explicitly enabled + if (process.env.CI || process.env.ENABLE_COVERAGE === 'true') { + require('@cypress/code-coverage/task')(on, config) + } // // It's IMPORTANT to return the config object // // with any changed environment variables @@ -26,7 +29,7 @@ export default defineConfig({ './cypress/tests/07-*/*.ts', './cypress/tests/03-*/*.ts', './cypress/tests/04-*/*.ts', - // './cypress/tests/05-*/*.ts', + './cypress/tests/05-*/*.ts', './cypress/tests/08-*/*.ts', './cypress/tests/09-*/*.ts', './cypress/tests/10-*/*.ts', @@ -74,6 +77,10 @@ export default defineConfig({ WEBAPP_URL: 'http://html-sample-app.localtest.me:4242', DEV_USERNAME: 'janis@idir', DEV_PASSWORD: 'awsummer', + HAS_KUBE_API: false, + // Pass CI flag to browser context for code coverage + CI: process.env.CI || false, + ENABLE_COVERAGE: process.env.ENABLE_COVERAGE || false, }, retries: { runMode: 2, diff --git a/e2e/cypress/fixtures/cc-service-gwa.yml b/e2e/cypress/fixtures/cc-service-gwa.yml index 77b815834..c07ee7fc0 100644 --- a/e2e/cypress/fixtures/cc-service-gwa.yml +++ b/e2e/cypress/fixtures/cc-service-gwa.yml @@ -1,6 +1,6 @@ services: - name: cc-service-for-platform - host: httpbin.org + host: httpbun.com tags: [ns.ccplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/cc-service.yml b/e2e/cypress/fixtures/cc-service.yml index 77b815834..c07ee7fc0 100644 --- a/e2e/cypress/fixtures/cc-service.yml +++ b/e2e/cypress/fixtures/cc-service.yml @@ -1,6 +1,6 @@ services: - name: cc-service-for-platform - host: httpbin.org + host: httpbun.com tags: [ns.ccplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/org-service.yml b/e2e/cypress/fixtures/org-service.yml index e252f20ab..c1443e1af 100644 --- a/e2e/cypress/fixtures/org-service.yml +++ b/e2e/cypress/fixtures/org-service.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-orgassignment - host: httpbin.org + host: httpbun.com tags: [ns.orgassignment] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-api.yml b/e2e/cypress/fixtures/service-api.yml index 668c9447a..7e0a7e3e3 100644 --- a/e2e/cypress/fixtures/service-api.yml +++ b/e2e/cypress/fixtures/service-api.yml @@ -1,6 +1,6 @@ services: - name: service-for-apiplatform - host: httpbin.org + host: httpbun.com tags: [ns.apiplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-availability.yml b/e2e/cypress/fixtures/service-availability.yml index 64851f962..e09c789c7 100644 --- a/e2e/cypress/fixtures/service-availability.yml +++ b/e2e/cypress/fixtures/service-availability.yml @@ -1,6 +1,6 @@ kind: GatewayService name: taken-service-name -host: httpbin.org +host: httpbun.com tags: [ns.service-avail] port: 446 protocol: https diff --git a/e2e/cypress/fixtures/service-clear-resources-gwa.yml b/e2e/cypress/fixtures/service-clear-resources-gwa.yml index 11a83ad3d..af7d12300 100644 --- a/e2e/cypress/fixtures/service-clear-resources-gwa.yml +++ b/e2e/cypress/fixtures/service-clear-resources-gwa.yml @@ -1,6 +1,6 @@ services: - name: service-for-deleteplatform - host: httpbin.org + host: httpbun.com tags: [ns.deleteplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-clear-resources.yml b/e2e/cypress/fixtures/service-clear-resources.yml index 11a83ad3d..af7d12300 100644 --- a/e2e/cypress/fixtures/service-clear-resources.yml +++ b/e2e/cypress/fixtures/service-clear-resources.yml @@ -1,6 +1,6 @@ services: - name: service-for-deleteplatform - host: httpbin.org + host: httpbun.com tags: [ns.deleteplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-dev.yml b/e2e/cypress/fixtures/service-dev.yml index 307cb9230..9210d562d 100644 --- a/e2e/cypress/fixtures/service-dev.yml +++ b/e2e/cypress/fixtures/service-dev.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-newplatform - host: httpbin.org + host: httpbun.com tags: [ns.newplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-gwa.yml b/e2e/cypress/fixtures/service-gwa.yml index 77ddda761..65aa62a16 100644 --- a/e2e/cypress/fixtures/service-gwa.yml +++ b/e2e/cypress/fixtures/service-gwa.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-newplatform - host: httpbin.org + host: httpbun.com tags: [ns.newplatform] port: 443 protocol: https @@ -19,7 +19,7 @@ services: path_handling: v0 - name: a-service-for-newplatform-test - host: httpbin.org + host: httpbun.com tags: [ns.newplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-permission-gwa.yml b/e2e/cypress/fixtures/service-permission-gwa.yml index cbcf1e7c7..f67a2e3a7 100644 --- a/e2e/cypress/fixtures/service-permission-gwa.yml +++ b/e2e/cypress/fixtures/service-permission-gwa.yml @@ -1,6 +1,6 @@ services: - name: service-for-permission - host: httpbin.org + host: httpbun.com tags: [ns.permission] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-permission.yml b/e2e/cypress/fixtures/service-permission.yml index 5bfbc2e65..aaee79197 100644 --- a/e2e/cypress/fixtures/service-permission.yml +++ b/e2e/cypress/fixtures/service-permission.yml @@ -1,6 +1,6 @@ services: - name: service-for-permission - host: httpbin.org + host: httpbun.com tags: [ns.permission] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-plugin_A.yml b/e2e/cypress/fixtures/service-plugin_A.yml index 4e56a2c42..ba6e0924c 100644 --- a/e2e/cypress/fixtures/service-plugin_A.yml +++ b/e2e/cypress/fixtures/service-plugin_A.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-newplatform - host: httpbin.org + host: httpbun.com tags: [ns.newplatform.test] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-plugin_B.yml b/e2e/cypress/fixtures/service-plugin_B.yml index 0137af191..581c9d9de 100644 --- a/e2e/cypress/fixtures/service-plugin_B.yml +++ b/e2e/cypress/fixtures/service-plugin_B.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-newplatform-test - host: httpbin.org + host: httpbun.com tags: [ns.newplatform.test] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/service-test.yml b/e2e/cypress/fixtures/service-test.yml index 5ed35b8bd..33199fa4d 100644 --- a/e2e/cypress/fixtures/service-test.yml +++ b/e2e/cypress/fixtures/service-test.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-newplatform-test - host: httpbin.org + host: httpbun.com tags: [ns.newplatform] port: 446 protocol: https diff --git a/e2e/cypress/fixtures/service.yml b/e2e/cypress/fixtures/service.yml index 77ddda761..65aa62a16 100644 --- a/e2e/cypress/fixtures/service.yml +++ b/e2e/cypress/fixtures/service.yml @@ -1,6 +1,6 @@ services: - name: a-service-for-newplatform - host: httpbin.org + host: httpbun.com tags: [ns.newplatform] port: 443 protocol: https @@ -19,7 +19,7 @@ services: path_handling: v0 - name: a-service-for-newplatform-test - host: httpbin.org + host: httpbun.com tags: [ns.newplatform] port: 443 protocol: https diff --git a/e2e/cypress/fixtures/tthidden-jwt.yml b/e2e/cypress/fixtures/tthidden-jwt.yml index 31c73a15d..ca2e9e015 100644 --- a/e2e/cypress/fixtures/tthidden-jwt.yml +++ b/e2e/cypress/fixtures/tthidden-jwt.yml @@ -5,7 +5,7 @@ displayName: two-tier-hidden Display Name kind: GatewayService name: two-tier-service-dev tags: [ns.two-tier-hidden] -host: httpbin.org +host: httpbun.com port: 443 protocol: https retries: 0 diff --git a/e2e/cypress/fixtures/tthidden-key-auth.yml b/e2e/cypress/fixtures/tthidden-key-auth.yml index 1cea3ff7d..f15cd551e 100644 --- a/e2e/cypress/fixtures/tthidden-key-auth.yml +++ b/e2e/cypress/fixtures/tthidden-key-auth.yml @@ -5,7 +5,7 @@ displayName: two-tier-hidden Display Name kind: GatewayService name: two-tier-service-dev tags: [ns.two-tier-hidden] -host: httpbin.org +host: httpbun.com port: 443 protocol: https retries: 0 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 c6c8dfde8..83d010874 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 @@ -166,7 +166,10 @@ it('Verify gwa gateway publish multiple config file', () => { }) }) - it('verify status of the services using "gwa status" command', () => { + // TODO: Remove this once we have a way to test the status command with the kube API in e2e tests + const runIfKubeAPI = Cypress.env('HAS_KUBE_API') ? it : it.skip; + + runIfKubeAPI('verify status of the services using "gwa status" command', () => { cy.get('@apiowner').then(({ product }: any) => { cy.executeCliCommand('gwa status').then((response) => { expect(response.stdout).to.contain(product.environment.config.serviceName); 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 696766a2a..f0bd3ee96 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 @@ -57,7 +57,7 @@ describe('Verify Gateway Service details', () => { }) it('Verify the host details ', () => { - gs.verifyHostName('httpbin.org') + gs.verifyHostName('httpbun.com') }) it('Verify the Tags details ', () => { diff --git a/e2e/cypress/tests/06-refresh-credential/02-client-credentials.cy.ts b/e2e/cypress/tests/06-refresh-credential/02-client-credentials.cy.ts index d159321cb..d11dbb101 100644 --- a/e2e/cypress/tests/06-refresh-credential/02-client-credentials.cy.ts +++ b/e2e/cypress/tests/06-refresh-credential/02-client-credentials.cy.ts @@ -86,7 +86,7 @@ describe('Regenerate Credential for Client Credentials- Client ID/Secret', () => }) }) - it('Regenrate credential client ID and Secret', () => { + it('Regenerate credential client ID and Secret', () => { cy.visit(myAccessPage.path) cy.get('@developer').then(({ clientCredentials }: any) => { myAccessPage.regenerateCredential(clientCredentials.clientIdSecret.product.environment, clientCredentials.clientIdSecret.application.name) 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 43c279d7b..bde1c51a4 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 @@ -50,7 +50,7 @@ describe('Manage Control-IP Restriction Spec', () => { cy.get('@apiowner').then(({ product }: any) => { cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { expect(response.status).to.be.equal(403) - expect(response.body.message).to.be.contain('Your IP address is not allowed') + expect(response.body.message).to.be.contain('IP address not allowed') }) }) }) @@ -69,7 +69,7 @@ describe('Manage Control-IP Restriction Spec', () => { cy.get('@apiowner').then(({ product }: any) => { cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { expect(response.status).to.be.equal(403) - expect(response.body.message).to.be.contain('Your IP address is not allowed') + expect(response.body.message).to.be.contain('IP address not allowed') }) }) }) @@ -160,7 +160,7 @@ describe('Manage Control -Apply IP Restriction to Global and Consumer at Service cy.get('@apiowner').then(({ product }: any) => { cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { expect(response.status).to.be.equal(403) - expect(response.body.message).to.be.contain('Your IP address is not allowed') + expect(response.body.message).to.be.contain('IP address not allowed') }) }) }) @@ -217,7 +217,7 @@ describe('Manage Control -Apply IP Restriction to Global and Consumer at Route l cy.get('@apiowner').then(({ product }: any) => { cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { expect(response.status).to.be.equal(403) - expect(response.body.message).to.be.contain('Your IP address is not allowed') + expect(response.body.message).to.be.contain('IP address not allowed') }) }) }) 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 292b9eded..b30965803 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 @@ -116,6 +116,7 @@ describe('Manage Control-Rate Limiting Spec for Service as Scope and Redis Polic cy.get('@manage-control-config-setting').then(({ rateLimiting }: any) => { cy.visit(consumers.path); consumers.clickOnTheFirstConsumerID() + consumers.clearRateLimitControl() consumers.setRateLimiting(rateLimiting.requestPerHour_Consumer, "Service", "Redis") }) }) @@ -149,6 +150,7 @@ describe('Manage Control-Rate Limiting Spec for Route as Scope and Redis Policy' cy.get('@manage-control-config-setting').then(({ rateLimiting }: any) => { cy.visit(consumers.path); consumers.clickOnTheFirstConsumerID() + consumers.clearRateLimitControl() consumers.setRateLimiting(rateLimiting.requestPerHour_Consumer, "Route", "Redis") }) }) @@ -178,7 +180,7 @@ describe('Manage Control-Apply Rate limiting to Global and Consumer at Service l cy.fixture('manage-control-config-setting').as('manage-control-config-setting') cy.visit(login.path) }) - + it('set api rate limit to global service level', () => { cy.visit(consumers.path); consumers.clickOnTheFirstConsumerID() @@ -197,11 +199,15 @@ describe('Manage Control-Apply Rate limiting to Global and Consumer at Service l }) }) + it('Clear Redis rate limit keys before setting new rate limit', () => { + cy.exec("docker exec redis-master sh -c \"export REDISCLI_AUTH=s3cr3t && redis-cli --scan --pattern 'ratelimit*' | xargs -r redis-cli DEL\"", { failOnNonZeroExit: false }) + }) + it('set api rate limit as per the test config, Redis Policy and Scope as Service', () => { cy.get('@manage-control-config-setting').then(({ rateLimiting }: any) => { cy.visit(consumers.path); consumers.clickOnTheFirstConsumerID() - consumers.setRateLimiting(rateLimiting.requestPerHour_Global, "Service", "Redis") + consumers.setRateLimiting(rateLimiting.requestPerHour_Consumer, "Service", "Redis") }) }) @@ -249,12 +255,16 @@ describe('Manage Control-Apply Rate limiting to Global and Consumer at Route lev }) }) }) + + it('Clear Redis rate limit keys before setting new rate limit', () => { + cy.exec("docker exec redis-master sh -c \"export REDISCLI_AUTH=s3cr3t && redis-cli --scan --pattern 'ratelimit*' | xargs -r redis-cli DEL\"", { failOnNonZeroExit: false }) + }) it('set api rate limit as per the test config, Redis Policy and Scope as Service', () => { cy.get('@manage-control-config-setting').then(({ rateLimiting }: any) => { cy.visit(consumers.path); consumers.clickOnTheFirstConsumerID() - consumers.setRateLimiting(rateLimiting.requestPerHour_Global, "Route", "Redis") + consumers.setRateLimiting(rateLimiting.requestPerHour_Consumer, "Route", "Redis") }) }) 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 8de053b68..6c32a472d 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 @@ -125,7 +125,7 @@ describe('Check the API key for free access', () => { const key = creds.consumerKey cy.makeKongRequest(product.environment.config.serviceName, 'GET', '').then((response) => { expect(response.status).to.be.equal(200) - expect(parseInt(response.headers["x-ratelimit-remaining-hour"])).to.be.equal(99) + expect(parseInt(response.headers["x-ratelimit-remaining-hour"])).to.be.within(97, 99) }) }) }) 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 eebd440b5..bf5ef5576 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 @@ -150,6 +150,7 @@ describe('Update Kong plugin and verify that only only GET method is allowed for }) it('Make "GET" call and verify that Kong allows user to access the resources', () => { + cy.wait(4000) cy.makeKongGatewayRequestUsingClientIDSecret('cc-service-for-platform.api.gov.bc.ca').then((response) => { cy.log(response) expect(response.status).to.be.equal(200) 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 70ac86ea3..4a6c976c5 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 @@ -106,7 +106,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', () => { +describe('Update Kong plugin and verify that only only PUT and POST methods are allowed for Write role', () => { beforeEach(() => { cy.fixture('apiowner').as('apiowner') cy.fixture('common-testdata').as('common-testdata') @@ -140,6 +140,7 @@ describe('Update Kong plugin and verify that only only PUT and POST methods are }) it('Make "GET" call and verify that Kong does not allow user to access the resources', () => { + cy.wait(4000) cy.makeKongGatewayRequestUsingClientIDSecret('cc-service-for-platform.api.gov.bc.ca').then((response) => { cy.log(response) expect(response.status).to.be.equal(404) @@ -150,14 +151,14 @@ describe('Update Kong plugin and verify that only only PUT and POST methods are it('Make "POST" call and verify that Kong allows user to access the resources', () => { cy.makeKongGatewayRequestUsingClientIDSecret('cc-service-for-platform.api.gov.bc.ca', 'POST').then((response) => { cy.log(response) - expect(response.status).to.be.equal(405) + expect(response.status).to.be.equal(200) }) }) it('Make "PUT" call and verify that Kong allows user to access the resources', () => { cy.makeKongGatewayRequestUsingClientIDSecret('cc-service-for-platform.api.gov.bc.ca', 'PUT').then((response) => { cy.log(response) - expect(response.status).to.be.equal(405) + expect(response.status).to.be.equal(200) }) }) diff --git a/e2e/cypress/tests/09-update-product-env/09-two-tiered-hidden.cy.ts b/e2e/cypress/tests/09-update-product-env/09-two-tiered-hidden.cy.ts index b02bf3447..12cb7d7f4 100644 --- a/e2e/cypress/tests/09-update-product-env/09-two-tiered-hidden.cy.ts +++ b/e2e/cypress/tests/09-update-product-env/09-two-tiered-hidden.cy.ts @@ -59,8 +59,23 @@ describe('Verify Two Tiered Hidden', () => { it('Upload config for key-auth', () => { cy.executeCliCommand('gwa apply -i ./cypress/fixtures/tthidden-key-auth.yml').then((response) => { - expect(response.stdout).to.contain('Gateway Services published'); - }) + // fix for bug observed in e2e tests + if ( + response.stdout.includes("2/3 Published, 1 Skipped") && (response.stdout.match(/\bcreated\b/g) || []).length === 1 + ) { + // Product not created because GatewayService is not synced yet: retry + cy.log('Retry apply config') + cy.executeCliCommand('gwa apply -i ./cypress/fixtures/tthidden-key-auth.yml').then((retryResponse) => { + expect(retryResponse.stdout).to.contain("3/3 Published, 1 Skipped"); + let wordOccurrences = (retryResponse.stdout.match(/\bcreated\b/g) || []).length; + expect(wordOccurrences).to.equal(1); + }); + } else { + expect(response.stdout).to.contain("3/3 Published, 1 Skipped"); + let wordOccurrences = (response.stdout.match(/\bcreated\b/g) || []).length; + expect(wordOccurrences).to.equal(2); + } + }); }) it('Activates the namespace', () => { @@ -80,7 +95,7 @@ describe('Verify Two Tiered Hidden', () => { cy.get('@apiowner').then(({ twoTieredHidden }: any) => { let product = twoTieredHidden.product apiDir.selectProduct(product.serviceName) - apiDir.checkProductIcon(product.name, 'RiEarthFill') + apiDir.checkProductIcon(product.name, 'FaLock') apiDir.checkTwoTieredHiddenButton() }) }) @@ -91,7 +106,7 @@ describe('Verify Two Tiered Hidden', () => { cy.get('@apiowner').then(({ twoTieredHidden }: any) => { let product = twoTieredHidden.product apiDir.selectProduct(product.serviceName) - apiDir.checkProductIcon(product.name, 'RiEarthFill') + apiDir.checkProductIcon(product.name, 'FaLock') apiDir.checkTwoTieredHiddenButton() }) }) @@ -107,18 +122,18 @@ describe('Verify Two Tiered Hidden', () => { cy.get('@apiowner').then(({ twoTieredHidden }: any) => { let product = twoTieredHidden.product apiDir.selectProduct(product.serviceName) - apiDir.checkProductIcon(product.name, 'RiEarthFill') + apiDir.checkProductIcon(product.name, 'FaLock') apiDir.checkTwoTieredHiddenButton() }) }) - + it('Verify that product is formatted correctly in your products page', () => { cy.visit(apiDir.path) apiDir.navigateToYourProduct() cy.get('@apiowner').then(({ twoTieredHidden }: any) => { let product = twoTieredHidden.product apiDir.selectProduct(product.serviceName) - apiDir.checkProductIcon(product.name, 'RiEarthFill') + apiDir.checkProductIcon(product.name, 'FaLock') apiDir.checkTwoTieredHiddenButton() }) }) 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 6d46e831f..c62e2e123 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 @@ -47,6 +47,58 @@ describe('Grant Namespace View Role to Mark', () => { }) }) +describe('Verify that Mark is unable to view Gateway summary', () => { + + const login = new LoginPage() + const home = new HomePage() + const consumers = new ConsumersPage() + const sa = new ServiceAccountsPage() + let userSession: any + let nameSpace: string + + before(() => { + cy.visit('/') + cy.reload(true) + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('api').as('api') + cy.fixture('access-manager').as('access-manager') + cy.fixture('common-testdata').as('common-testdata') + }) + + it('authenticates Mark', () => { + cy.get('@access-manager').then(({ user }: any) => { + cy.get('@common-testdata').then(({ checkPermission }: any) => { + cy.visit(login.path) + cy.login(user.credentials.username, user.credentials.password) + cy.log('Logged in!') + cy.activateGateway(checkPermission.namespace) + cy.getUserSessionResponse().then(( response: any ) => { + userSession = response.headers['x-auth-request-access-token'] + nameSpace = checkPermission.namespace + }) + }) + }) + }) + + it('Verify scopes are limited to those which Mark has been granted - Namespace.Manage scope missing', () => { + cy.get('@api').then(({ namespaces }: any) => { + cy.setHeaders(namespaces.headers) + cy.setAuthorizationToken(userSession) + cy.makeAPIRequest(namespaces.endPoint + "/" + nameSpace, 'GET').then((res:any) => { + expect(res.apiRes.status).to.be.equal(401) + expect(res.apiRes.body.message).to.be.contain('Missing authorization scope') + }) + }) + }) + + after(() => { + cy.logout() + }) +}) + describe('Verify that Mark is unable to create service account', () => { const login = new LoginPage() 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 afaa14800..3fe724547 100644 --- a/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts +++ b/e2e/cypress/tests/15-aps-api/08-namespaces.cy.ts @@ -237,7 +237,7 @@ describe('API Tests for invalid namespace name', () => { cy.makeAPIRequest(namespaces.endPoint, 'POST').then((res:any) => { expect(res.apiRes.status).to.be.equal(422) cy.addToAstraScanIdList(res.astraRes.body.status) - expect(res.apiRes.body.message).to.be.equal('Validation Failed') + expect(res.apiRes.body.message).to.be.equal('Unable to create gateway') }) }) }) diff --git a/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config-quick-start.ts b/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config-quick-start.ts index b19f5d0aa..ea2f52f69 100644 --- a/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config-quick-start.ts +++ b/e2e/cypress/tests/16-gwa-cli/02-cli-generate-config-quick-start.ts @@ -47,11 +47,11 @@ describe('Verify CLI commands for generate/apply config', () => { }); }) - it('Check gwa command to generate config for client credential template', () => { + it('Check gwa command to generate config for quick start template', () => { const command = [ 'gwa generate-config --template quick-start', `--service ${serviceName}`, - '--upstream https://httpbin.org', + '--upstream https://httpbun.com', '--org ministry-of-health', '--org-unit planning-and-innovation-division', '--out gw-config-qs.yaml' @@ -63,13 +63,29 @@ describe('Verify CLI commands for generate/apply config', () => { it('Check gwa command to apply generated config', () => { cy.executeCliCommand('gwa apply -i gw-config-qs.yaml').then((response) => { - expect(response.stdout).to.contain("3/3 Published, 0 Skipped") - let wordOccurrences = (response.stdout.match(/\bcreated\b/g) || []).length; - expect(wordOccurrences).to.equal(2) + // fix for bug observed in e2e tests + if ( + response.stdout.includes("2/3 Published, 0 Skipped") && (response.stdout.match(/\bcreated\b/g) || []).length === 1 + ) { + // Product not created because GatewayService is not synced yet: retry + cy.log('Retry apply config') + cy.executeCliCommand('gwa apply -i gw-config-qs.yaml').then((retryResponse) => { + expect(retryResponse.stdout).to.contain("3/3 Published, 0 Skipped"); + let wordOccurrences = (retryResponse.stdout.match(/\bcreated\b/g) || []).length; + expect(wordOccurrences).to.equal(1); + }); + } else { + expect(response.stdout).to.contain("3/3 Published, 0 Skipped"); + let wordOccurrences = (response.stdout.match(/\bcreated\b/g) || []).length; + expect(wordOccurrences).to.equal(2); + } }); }) - it('Check gwa status --hosts include routes', () => { + // TODO: Remove this once we have a way to test the status command with the kube API in e2e tests + const runIfKubeAPI = Cypress.env('HAS_KUBE_API') ? it : it.skip; + + runIfKubeAPI('Check gwa status --hosts include routes', () => { cy.executeCliCommand('gwa status --hosts').then((response) => { expect(response.stdout).to.contain('https://' + serviceName + '.dev.api.gov.bc.ca') }); @@ -96,7 +112,7 @@ describe('Verify CLI commands for generate/apply config', () => { const command = [ 'gwa generate-config --template quick-start', `--service ${serviceName}`, - '--upstream https://httpbin.org', + '--upstream https://httpbun.com', '--org ministry-of-health', '--org-unit planning-and-innovation-division' ].join(' '); diff --git a/e2e/cypress/tests/16-gwa-cli/03-cli-generate-config-client-cred.ts b/e2e/cypress/tests/16-gwa-cli/03-cli-generate-config-client-cred.ts index fa0e28413..5750b2a61 100644 --- a/e2e/cypress/tests/16-gwa-cli/03-cli-generate-config-client-cred.ts +++ b/e2e/cypress/tests/16-gwa-cli/03-cli-generate-config-client-cred.ts @@ -52,7 +52,7 @@ describe('Verify CLI commands for generate/apply config', () => { const command = [ 'gwa generate-config --template client-credentials-shared-idp', `--service ${serviceName}`, - '--upstream https://httpbin.org', + '--upstream https://httpbun.com', '--org ministry-of-health', '--org-unit planning-and-innovation-division', '--out gw-config-cc.yaml' 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 143461f4e..12cbb85d8 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 @@ -65,7 +65,7 @@ describe('Verify namespace delete using gwa command', () => { cy.executeCliCommand('gwa config set --gateway ' + namespace).then((response) => { expect(response.stdout).to.contain("Config settings saved") cy.executeCliCommand('gwa gateway destroy').then((response) => { - expect(response.stderr).to.contain('Error: Validation Failed'); + expect(response.stderr).to.contain('Error: Unable to delete gateway'); }); }) }) @@ -73,7 +73,7 @@ describe('Verify namespace delete using gwa command', () => { it('Check validation if any consumer is associated with namespace for hard deleting the namespace', () => { cy.executeCliCommand('gwa gateway destroy --force').then((response) => { - expect(response.stderr).to.contain('Error: Validation Failed'); + expect(response.stderr).to.contain('Error: Unable to delete gateway'); }); }) diff --git a/e2e/cypress/tests/19-api-v3/01-api-directory.ts b/e2e/cypress/tests/19-api-v3/01-api-directory.ts index 6ad03ef39..48a0c6f58 100644 --- a/e2e/cypress/tests/19-api-v3/01-api-directory.ts +++ b/e2e/cypress/tests/19-api-v3/01-api-directory.ts @@ -138,7 +138,7 @@ describe('API Directory', () => { { name: `my-product-on-${gateway.gatewayId}`, environments: [ - { name: 'dev', active: true, flow: 'public', services: [] }, + { name: 'dev', active: true, flow: 'public' }, ], }, ], diff --git a/e2e/cypress/tests/19-api-v3/03-gateways.ts b/e2e/cypress/tests/19-api-v3/03-gateways.ts index e0918bdbc..a2544a7be 100644 --- a/e2e/cypress/tests/19-api-v3/03-gateways.ts +++ b/e2e/cypress/tests/19-api-v3/03-gateways.ts @@ -184,8 +184,9 @@ describe('Gateways', () => { cy.callAPI('ds/api/v3/gateways', 'POST').then( ({ apiRes: { body, status } }: any) => { const match = { - message: 'Validation Failed', - details: { + code: 'validation_error', + message: 'Unable to create Gateway', + fields: { d0: { message: 'Gateway ID must be between 5 and 15 lowercase alpha-numeric characters and start with a letter.', @@ -206,8 +207,9 @@ describe('Gateways', () => { cy.callAPI('ds/api/v3/gateways', 'POST').then( ({ apiRes: { body, status } }: any) => { const match = { - message: 'Validation Failed', - details: { + code: 'validation_error', + message: 'Unable to create Gateway', + fields: { d0: { message: 'Display name must be between 3 and 30 characters, starting with an alpha-numeric character, and can only use special characters "-()_ .\'/".', @@ -228,8 +230,9 @@ describe('Gateways', () => { cy.callAPI('ds/api/v3/gateways', 'POST').then( ({ apiRes: { body, status } }: any) => { const match = { - message: 'Validation Failed', - details: { + code: 'validation_error', + message: 'Unable to create Gateway', + fields: { d0: { message: 'Display name must be between 3 and 30 characters, starting with an alpha-numeric character, and can only use special characters "-()_ .\'/".', diff --git a/e2e/package.json b/e2e/package.json index c8c5664b1..1667e97dd 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -32,7 +32,7 @@ "@types/request": "^2.48.7", "@typescript-eslint/eslint-plugin": "^4.28.1", "@typescript-eslint/parser": "^4.28.1", - "cypress": "^13.0.0", + "cypress": "^13.17.0", "cypress-iframe": "^1.0.1", "cypress-mochawesome-reporter": "^3.2.3", "cypress-slow-down": "^1.2.1", diff --git a/local/db/keystone-init.sql b/local/db/keystone-init.sql index 25d905bf9..46c6f3c2f 100644 --- a/local/db/keystone-init.sql +++ b/local/db/keystone-init.sql @@ -5,6 +5,10 @@ -- Dumped from database version 12.2 -- Dumped by pg_dump version 12.2 +-- Disable pager to prevent hanging in non-interactive mode +\pset pager off +\set QUIET on + SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; diff --git a/local/gwa-api/.env.local b/local/gwa-api/.env.local index be995a043..106cbd50a 100644 --- a/local/gwa-api/.env.local +++ b/local/gwa-api/.env.local @@ -22,4 +22,5 @@ PORTAL_ACTIVITY_TOKEN= HOST_TRANSFORM_ENABLED=false HOST_TRANSFORM_BASE_URL= PLUGINS_RATELIMITING_REDIS_PASSWORD=s3cr3t -LOCAL_ENVIRONMENT=True \ No newline at end of file +LOCAL_ENVIRONMENT=True +DECK_CLI=deck \ No newline at end of file diff --git a/local/gwa-api/entrypoint.sh b/local/gwa-api/entrypoint.sh index e6b975ef8..045718f3a 100755 --- a/local/gwa-api/entrypoint.sh +++ b/local/gwa-api/entrypoint.sh @@ -46,7 +46,8 @@ cat > "${CONFIG_PATH:-./config/default.json}" < { + prod.environments.forEach((env) => { + removeKeys(env, ['services']); + }); + }); + return products; +} + +export function transformSetTwoTieredHidden(products: Product[]) { + products.forEach((prod) => { + prod.environments.forEach((env) => { + env.services.forEach((svc) => { + svc.plugins?.forEach((plugin) => { + if (plugin.tags) { + // Tags come from GraphQL as JSON strings, need to parse them + const tags: string[] = JSON.parse(plugin.tags); + if (tags.includes('aps.two-tiered-hidden')) { + (env as any).twoTieredHidden = true; + } + } + }); + }); + }); + }); + return products; +} + export function transformSetAnonymous(products: Product[]) { products.forEach((prod) => { prod.environments.forEach((env) => { diff --git a/src/controllers/v2/NamespaceDirectoryController.ts b/src/controllers/v2/NamespaceDirectoryController.ts index 359673e9c..9d9229b76 100644 --- a/src/controllers/v2/NamespaceDirectoryController.ts +++ b/src/controllers/v2/NamespaceDirectoryController.ts @@ -10,7 +10,11 @@ import { } from 'tsoa'; import { KeystoneService } from '../ioc/keystoneInjector'; import { inject, injectable } from 'tsyringe'; -import { transform, transformSetAnonymous } from './DirectoryController'; +import { + transform, + transformSetAnonymous, + transformSetTwoTieredHidden, +} from './DirectoryController'; import { gql } from 'graphql-request'; import { strict as assert } from 'assert'; @@ -55,7 +59,9 @@ export class NamespaceDirectoryController extends Controller { ); return transform( - transformSetAnonymous(result.data.allProductsByNamespace) + transformSetTwoTieredHidden( + transformSetAnonymous(result.data.allProductsByNamespace) + ) )[0]; } diff --git a/src/controllers/v3/DirectoryController.ts b/src/controllers/v3/DirectoryController.ts index 1d6cf5e53..cbe2d6f68 100644 --- a/src/controllers/v3/DirectoryController.ts +++ b/src/controllers/v3/DirectoryController.ts @@ -43,11 +43,43 @@ export class DirectoryController extends Controller { } return transform( - transformSetAnonymous(result.data.allDiscoverableProducts) + transformRemoveServices( + transformSetTwoTieredHidden( + transformSetAnonymous(result.data.allDiscoverableProducts) + ) + ) )[0]; } } +export function transformRemoveServices(products: Product[]) { + products.forEach((prod) => { + prod.environments.forEach((env) => { + removeKeys(env, ['services']); + }); + }); + return products; +} + +export function transformSetTwoTieredHidden(products: Product[]) { + products.forEach((prod) => { + prod.environments.forEach((env) => { + env.services.forEach((svc) => { + svc.plugins?.forEach((plugin) => { + if (plugin.tags) { + // Tags come from GraphQL as JSON strings, need to parse them + const tags: string[] = JSON.parse(plugin.tags); + if (tags.includes('aps.two-tiered-hidden')) { + (env as any).twoTieredHidden = true; + } + } + }); + }); + }); + }); + return products; +} + export function transformSetAnonymous(products: Product[]) { products.forEach((prod) => { prod.environments.forEach((env) => { diff --git a/src/controllers/v3/GatewayConfigController.ts b/src/controllers/v3/GatewayConfigController.ts new file mode 100644 index 000000000..1c12615e3 --- /dev/null +++ b/src/controllers/v3/GatewayConfigController.ts @@ -0,0 +1,99 @@ +import { + Controller, + Request, + Response, + OperationId, + Get, + Put, + Path, + Route, + Security, + Body, + Tags, + Example, + ValidateError, + SuccessResponse, +} from 'tsoa'; +import { KeystoneService } from '../ioc/keystoneInjector'; +import { inject, injectable } from 'tsyringe'; +import { GetConfigUsingPattern } from '../../services/gateway-patterns/evaluator'; + +/** + * @example { + * "pattern": "simple-service.r1", + * "parameters": { + * "service_name": "my-service", + * "service_url": "https://httpbun.com" + * } + * } + */ +interface GatewayPatternConfigRequest { + pattern: string; + parameters: { [key: string]: string }; +} + +interface UnauthorizedJSON { + code: 'invalid_token'; + message: 'Missing authorization scope. (403)'; +} + +interface ValidateErrorJSON { + code: 'validation_error'; + message: 'Invalid input'; + fields: { [name: string]: { message: string } }; +} + +@injectable() +@Route('/gateways/{gatewayId}') +@Tags('Gateways') +export class GatewayConfigController extends Controller { + private keystone: KeystoneService; + constructor(@inject('KeystoneService') private _keystone: KeystoneService) { + super(); + this.keystone = _keystone; + } + + /** + * > `Required Scope:` GatewayConfig.Publish + * + * @summary Generate gateway config from pre-defined patterns + */ + @Put('/pattern') + @OperationId('generate-config-from-pattern') + @Security('jwt', ['GatewayConfig.Publish']) + @SuccessResponse('200', 'OK') + @Example({ + documents: [ + { + kind: 'GatewayService', + name: 'sdx.my-service', + routes: [], + }, + ], + }) + @Response(401, 'Unauthorized', { + code: 'invalid_token', + message: 'Missing authorization scope. (403)', + }) + @Response(422, 'Validation Failed', { + code: 'validation_error', + message: 'Invalid input', + fields: { + pattern: { + message: 'unsupported pattern', + }, + }, + }) + public async generateConfigFromPattern( + @Path() gatewayId: string, + @Body() body: GatewayPatternConfigRequest, + @Request() request: any + ): Promise { + // always inject the gatewayId as a parameter + body.parameters.gateway_id = gatewayId; + + const ctx = this.keystone.createContext(request); + + return await GetConfigUsingPattern(ctx, body); + } +} diff --git a/src/controllers/v3/GatewayDirectoryController.ts b/src/controllers/v3/GatewayDirectoryController.ts index fbbb44bbf..df8fad643 100644 --- a/src/controllers/v3/GatewayDirectoryController.ts +++ b/src/controllers/v3/GatewayDirectoryController.ts @@ -10,7 +10,11 @@ import { } from 'tsoa'; import { KeystoneService } from '../ioc/keystoneInjector'; import { inject, injectable } from 'tsyringe'; -import { transform, transformSetAnonymous } from './DirectoryController'; +import { + transform, + transformSetAnonymous, + transformSetTwoTieredHidden, +} from './DirectoryController'; import { gql } from 'graphql-request'; import { strict as assert } from 'assert'; @@ -55,7 +59,9 @@ export class GatewayDirectoryController extends Controller { ); return transform( - transformSetAnonymous(result.data.allProductsByNamespace) + transformSetTwoTieredHidden( + transformSetAnonymous(result.data.allProductsByNamespace) + ) )[0]; } 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 469e10f0f..d5be7c74b 100644 --- a/src/nextapp/components/api-product-item/api-product-item.tsx +++ b/src/nextapp/components/api-product-item/api-product-item.tsx @@ -18,6 +18,7 @@ import { Dataset, Environment, Product } from '@/shared/types/query.types'; export interface ApiEnvironment extends Environment { anonymous: boolean; + twoTieredHidden?: boolean; } export interface ApiProduct extends Product { @@ -45,11 +46,7 @@ const ApiProductItem: React.FC = ({ ); const isTiered = data.environments.some((e) => e.anonymous); - const isTieredHidden = data.environments.some((e) => - e.services.some((s) => - s.plugins.some((p) => p.tags.includes('aps.two-tiered-hidden')) - ) - ); + const isTieredHidden = data.environments.some((e) => e.twoTieredHidden); return ( <> @@ -59,11 +56,11 @@ const ApiProductItem: React.FC = ({ diff --git a/src/package-lock.json b/src/package-lock.json index e452672db..a8723dc0c 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -60,6 +60,7 @@ "keycloak-connect": "^17.0.1", "lodash": "^4.17.21", "multer": "^1.4.2", + "node-fetch": "^2.7.0", "nodemailer": "^6.6.0", "npmlog": "^6.0.1", "numeral": "^2.0.6", @@ -153,7 +154,7 @@ "typescript": "4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=22.0.0 <23.0.0" } }, "node_modules/@ampproject/remapping": { @@ -5471,6 +5472,15 @@ "node-fetch": "2.6.1" } }, + "node_modules/@graphql-tools/links/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@graphql-tools/load": { "version": "6.2.8", "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-6.2.8.tgz", @@ -5841,6 +5851,15 @@ "node-fetch": "2.6.1" } }, + "node_modules/@graphql-tools/url-loader/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@graphql-tools/url-loader/node_modules/tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", @@ -9512,6 +9531,15 @@ } } }, + "node_modules/@keystonejs/app-admin-ui/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@keystonejs/app-admin-ui/node_modules/pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -10239,6 +10267,15 @@ } } }, + "node_modules/@keystonejs/fields-auto-increment/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@keystonejs/fields-auto-increment/node_modules/pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -11060,6 +11097,15 @@ } } }, + "node_modules/@keystonejs/fields-mongoid/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@keystonejs/fields-mongoid/node_modules/pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -11804,6 +11850,15 @@ } } }, + "node_modules/@keystonejs/fields/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@keystonejs/fields/node_modules/pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -12844,6 +12899,15 @@ "node": ">=8" } }, + "node_modules/@prisma/sdk/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/@prisma/sdk/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -18813,12 +18877,13 @@ } }, "node_modules/babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "license": "MIT", "dependencies": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.4", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, @@ -18830,28 +18895,18 @@ "webpack": ">=2" } }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "json5": "^2.1.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=8.9.0" } }, "node_modules/babel-loader/node_modules/schema-utils": { @@ -20557,6 +20612,15 @@ "uuid": "8.3.0" } }, + "node_modules/checkpoint-client/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/checkpoint-client/node_modules/uuid": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", @@ -22052,6 +22116,15 @@ "node-fetch": "2.6.1" } }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -22078,30 +22151,6 @@ "web-streams-polyfill": "^3.2.0" } }, - "node_modules/cross-undici-fetch/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/cross-undici-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/cross-undici-fetch/node_modules/undici": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.0.0.tgz", @@ -22110,20 +22159,6 @@ "node": ">=12.18" } }, - "node_modules/cross-undici-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/cross-undici-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/cryptiles": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.2.tgz", @@ -37716,11 +37751,45 @@ } }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/node-fingerprint": { @@ -53337,6 +53406,11 @@ "requires": { "node-fetch": "2.6.1" } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" } } }, @@ -53666,6 +53740,11 @@ "node-fetch": "2.6.1" } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", @@ -56513,6 +56592,11 @@ "tildify": "2.0.0" } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -57232,6 +57316,11 @@ "tildify": "2.0.0" } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -57651,6 +57740,11 @@ "tildify": "2.0.0" } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -58311,6 +58405,11 @@ "tildify": "2.0.0" } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -59114,6 +59213,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -63769,32 +63873,24 @@ } }, "babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", "requires": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.4", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "json5": "^2.1.2" } }, "schema-utils": { @@ -65185,6 +65281,11 @@ "uuid": "8.3.0" }, "dependencies": { + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "uuid": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", @@ -66395,6 +66496,13 @@ "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", "requires": { "node-fetch": "2.6.1" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + } } }, "cross-spawn": { @@ -66420,37 +66528,10 @@ "web-streams-polyfill": "^3.2.0" }, "dependencies": { - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "undici": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.0.0.tgz", "integrity": "sha512-VhUpiZ3No1DOPPQVQnsDZyfcbTTcHdcgWej1PdFnSvOeJmOVDgiOHkunJmBLfmjt4CqgPQddPVjSWW0dsTs5Yg==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } } } }, @@ -78441,9 +78522,33 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } }, "node-fingerprint": { "version": "0.0.2", diff --git a/src/services/gateway-patterns/evaluator.ts b/src/services/gateway-patterns/evaluator.ts new file mode 100644 index 000000000..cf049aad5 --- /dev/null +++ b/src/services/gateway-patterns/evaluator.ts @@ -0,0 +1,50 @@ +import { FieldErrors, ValidateError } from 'tsoa'; +import { SimpleServicePattern } from './patterns/simple-service'; + +const PATTERNS = { + [SimpleServicePattern.id]: SimpleServicePattern, +}; + +export interface GatewayPatternConfig { + pattern: string; + delete?: boolean; + parameters: Record; +} + +export async function GetConfigUsingPattern( + ctx: any, + inputs: GatewayPatternConfig +): Promise { + if (PATTERNS[inputs.pattern]) { + const pattern = PATTERNS[inputs.pattern]; + expectRequiredParams(inputs.parameters, pattern.requiredParams); + return { documents: pattern.eval(inputs.parameters) }; + } else { + throw new ValidateError( + { + [inputs.pattern]: { + message: 'unsupported pattern', + }, + }, + 'Invalid input' + ); + } +} + +function expectRequiredParams( + provided: Record, + required: string[] +) { + const errors: FieldErrors = {}; + + for (const param of required) { + if (!provided[param]) { + errors[param] = { + message: 'missing required parameter', + }; + } + } + if (Object.keys(errors).length > 0) { + throw new ValidateError(errors, 'Invalid input'); + } +} diff --git a/src/services/gateway-patterns/patterns/simple-service.ts b/src/services/gateway-patterns/patterns/simple-service.ts new file mode 100644 index 000000000..9c258309a --- /dev/null +++ b/src/services/gateway-patterns/patterns/simple-service.ts @@ -0,0 +1,29 @@ +export interface SimpleServicePatternConfig extends Record { + gateway_id: string; + service_name: string; + service_url: string; +} + +export const SimpleServicePattern = { + id: 'simple-service.r1', + requiredParams: ['gateway_id', 'service_name', 'service_url'], + eval: (inputs: Record) => { + const nm = `sdx.${inputs.service_name}`; + const nsQualifier = inputs.service_name; + return [ + { + kind: 'GatewayService', + name: nm, + tags: [`ns.${inputs.gateway_id}.${nsQualifier}`], + url: inputs.service_url, + routes: [ + { + name: nm, + tags: [`ns.${inputs.gateway_id}.${nsQualifier}`], + hosts: [`${inputs.service_name}.dev.api.gov.bc.ca`], + }, + ], + }, + ]; + }, +}; diff --git a/src/test/services/batch/integrated-batch.test.ts b/src/test/services/batch/integrated-batch.test.ts index 711491160..c6b02d009 100644 --- a/src/test/services/batch/integrated-batch.test.ts +++ b/src/test/services/batch/integrated-batch.test.ts @@ -89,9 +89,11 @@ describe('Batch Tests', function () { } } catch (e) { logger.error(e.message); + logger.error('"%s" "%s"', test.expected?.exception, `${e.message}`); + // node 22+ assert includes the values used in the comparison if ( !test.expected?.exception || - test.expected?.exception != `${e.message}` + test.expected?.exception !== `${e.message}` ) { await keystone.disconnect(); diff --git a/src/test/services/batch/testdata.js b/src/test/services/batch/testdata.js index 103cff5ec..61046979a 100644 --- a/src/test/services/batch/testdata.js +++ b/src/test/services/batch/testdata.js @@ -81,7 +81,7 @@ export default { payload: { status: 400, result: 'update-failed', - reason: 'Failed updating children', + reason: 'Failed updating children\n\n1 !== 0\n', childResults: [ { status: 400, @@ -210,7 +210,7 @@ export default { data: { namespace: 'refactortime', }, - expected: { exception: 'Missing value for key name' }, + expected: { exception: 'Missing value for key name\n\nfalse !== true\n' }, }, { name: 'create a new product with lots of environments', diff --git a/src/test/services/gateway-patterns/evaluator.test.ts b/src/test/services/gateway-patterns/evaluator.test.ts new file mode 100644 index 000000000..07ed4578e --- /dev/null +++ b/src/test/services/gateway-patterns/evaluator.test.ts @@ -0,0 +1,50 @@ +import { GetConfigUsingPattern } from '../../../services/gateway-patterns/evaluator'; +import { logger } from '../../../logger'; + +describe('Gateway Simple Pattern', function () { + it('it should return a GatewayService configuration', async function () { + const patternConfig = { + pattern: 'simple-service.r1', + parameters: { + gateway_id: 'gw-12345', + service_name: 'test-service', + service_url: 'https://httpbun.com', + }, + }; + + const result = await GetConfigUsingPattern(undefined, patternConfig); + + const expected = { + documents: [ + { + kind: 'GatewayService', + name: 'sdx.test-service', + tags: ['ns.gw-12345.test-service'], + url: 'https://httpbun.com', + routes: [ + { + name: 'sdx.test-service', + tags: ['ns.gw-12345.test-service'], + hosts: ['test-service.dev.api.gov.bc.ca'], + }, + ], + }, + ], + }; + expect(result).toStrictEqual(expected); + }); + + it('it should return error missing param', async function () { + const patternConfig = { + pattern: 'simple-service.r1', + parameters: { + service_name: 'test-service', + service_url: 'https://httpbun.com', + }, + }; + + await expect( + GetConfigUsingPattern(undefined, patternConfig) + ).rejects.toThrow('Invalid input'); + }); +}); diff --git a/src/yarn.lock b/src/yarn.lock index d59147c0b..82eb79c8a 100644 --- a/src/yarn.lock +++ b/src/yarn.lock @@ -671,12 +671,13 @@ dependencies: tslib "~2.0.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.24.6", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz" + integrity sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.24.6" + picocolors "^1.0.0" "@babel/code-frame@7.10.4": version "7.10.4" @@ -784,12 +785,12 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== +"@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.16.7", "@babel/helper-annotate-as-pure@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz" + integrity sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.24.6" "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": version "7.16.7" @@ -810,17 +811,19 @@ semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.12.1", "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6", "@babel/helper-create-class-features-plugin@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz" - integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.6.tgz" + integrity sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.6" + "@babel/helper-environment-visitor" "^7.24.6" + "@babel/helper-function-name" "^7.24.6" + "@babel/helper-member-expression-to-functions" "^7.24.6" + "@babel/helper-optimise-call-expression" "^7.24.6" + "@babel/helper-replace-supers" "^7.24.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.6" + "@babel/helper-split-export-declaration" "^7.24.6" + semver "^6.3.1" "@babel/helper-create-regexp-features-plugin@^7.16.7": version "7.17.0" @@ -858,12 +861,10 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz" + integrity sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g== "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" @@ -872,13 +873,13 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.12.11", "@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz" - integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== +"@babel/helper-function-name@^7.12.11", "@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9", "@babel/helper-function-name@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz" + integrity sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w== dependencies: - "@babel/template" "^7.16.7" - "@babel/types" "^7.17.0" + "@babel/template" "^7.24.6" + "@babel/types" "^7.24.6" "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" @@ -887,12 +888,12 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz" - integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== +"@babel/helper-member-expression-to-functions@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.6.tgz" + integrity sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.24.6" "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.16.7": version "7.16.7" @@ -915,17 +916,17 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" -"@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== +"@babel/helper-optimise-call-expression@^7.16.7", "@babel/helper-optimise-call-expression@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.6.tgz" + integrity sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.24.6" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz" + integrity sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg== "@babel/helper-plugin-utils@7.10.4": version "7.10.4" @@ -941,16 +942,14 @@ "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== +"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.6.tgz" + integrity sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-environment-visitor" "^7.24.6" + "@babel/helper-member-expression-to-functions" "^7.24.6" + "@babel/helper-optimise-call-expression" "^7.24.6" "@babel/helper-simple-access@^7.10.4", "@babel/helper-simple-access@^7.17.7": version "7.17.7" @@ -959,24 +958,29 @@ dependencies: "@babel/types" "^7.17.0" -"@babel/helper-skip-transparent-expression-wrappers@^7.12.1", "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1", "@babel/helper-skip-transparent-expression-wrappers@^7.16.0", "@babel/helper-skip-transparent-expression-wrappers@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.6.tgz" + integrity sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.24.6" -"@babel/helper-split-export-declaration@^7.12.11", "@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== +"@babel/helper-split-export-declaration@^7.12.11", "@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz" + integrity sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.24.6" -"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-string-parser@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz" + integrity sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q== + +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz" + integrity sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw== "@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.14.5", "@babel/helper-validator-option@^7.16.7": version "7.16.7" @@ -1002,19 +1006,20 @@ "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.24.6": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz" + integrity sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.24.6" + chalk "^2.4.2" js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.12.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.3", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.9", "@babel/parser@^7.7.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz" - integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.12.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.3", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.17.9", "@babel/parser@^7.24.6", "@babel/parser@^7.7.7": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz" + integrity sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q== "@babel/parser@7.12.11": version "7.12.11" @@ -2149,16 +2154,16 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.3.3", "@babel/template@^7.7.4": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.24.6", "@babel/template@^7.3.3", "@babel/template@^7.7.4": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz" + integrity sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.24.6" + "@babel/parser" "^7.24.6" + "@babel/types" "^7.24.6" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2", "@babel/traverse@^7.7.4": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2", "@babel/traverse@^7.7.4": version "7.17.9" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz" integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== @@ -2189,12 +2194,13 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.12.0", "@babel/types@^7.12.1", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.17.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.0.0", "@babel/types@^7.12.0", "@babel/types@^7.12.1", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.2.0", "@babel/types@^7.24.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz" + integrity sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.24.6" + "@babel/helper-validator-identifier" "^7.24.6" to-fast-properties "^2.0.0" "@babel/types@^7.11.5", "@babel/types@^7.7.4", "@babel/types@7.11.5": @@ -8462,12 +8468,12 @@ babel-jest@^27.5.1: slash "^3.0.0" babel-loader@^8.0.0, babel-loader@^8.2.2: - version "8.2.2" - resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz" - integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g== + version "8.4.1" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz" + integrity sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA== dependencies: find-cache-dir "^3.3.1" - loader-utils "^1.4.0" + loader-utils "^2.0.4" make-dir "^3.1.0" schema-utils "^2.6.5" @@ -8814,13 +8820,6 @@ binary@~0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz" @@ -9451,7 +9450,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -13083,19 +13082,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - fstream@^1.0.12: version "1.0.12" resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz" @@ -16366,15 +16352,6 @@ loader-utils@^1.2.3: emojis-list "^3.0.0" json5 "^1.0.1" -loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - loader-utils@^2.0.0, loader-utils@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz" @@ -16384,6 +16361,15 @@ loader-utils@^2.0.0, loader-utils@2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + loader-utils@1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz" @@ -17607,11 +17593,6 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1: - version "2.14.2" - resolved "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - nanoassert@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz" @@ -17792,15 +17773,10 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" @@ -17809,6 +17785,11 @@ node-fetch@2.6.0: resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" @@ -21406,10 +21387,10 @@ semver@^5.7.1: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.2.1: version "7.3.6"