Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cypress updates #1056

Merged
merged 5 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ src/.npm
src/_tmp
src/.cache
src/.config
src/.nyc_output
src/dist
**/.next
_data
127 changes: 127 additions & 0 deletions .github/astra-jira.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import os
from datetime import datetime
import json
from pathlib import Path
import requests
from requests.auth import HTTPBasicAuth
import sys

ASTRA_SCAN_RESULTS = os.environ.get('ASTRA_SCAN_RESULTS')

JIRA_EMAIL = os.environ.get('JIRA_EMAIL')
JIRA_API_KEY = os.environ.get('JIRA_API_KEY')
JIRA_API_URL = "https://dpdd.atlassian.net/rest/api/2"
JIRA_AUTH = HTTPBasicAuth(JIRA_EMAIL, JIRA_API_KEY)
HEADERS = {
"Accept": "application/json",
"Content-Type": "application/json"
}

IMPACT_LEVELS = ["Medium", "High"]

date = datetime.now()
date_str = date.strftime("%d %b %Y")
scan_name = f'{date_str} - Astra Scan Results'

def check_results(scan_result):
"""
Check if there are any significant vulnerabilities
"""
vulnerabilities = [vulnerability for sublist in scan_result for vulnerability in sublist]
for vulnerability in vulnerabilities:
if vulnerability["impact"] in IMPACT_LEVELS:
print('Issues found!')
return True

return False

def format_ticket(scan_results):
"""
Converts vulnerabilities into format that can be posted to Jira.
"""
description = 'See attached scan results for more details'
for sublist in scan_results:
for vulnerability in sublist:
if vulnerability["impact"] in IMPACT_LEVELS:
description += f'\n\n*Name: {vulnerability["name"]}*\n'
description += f'Impact: {vulnerability["impact"]}\n'
description += f'Description: {vulnerability["Description"]}\n'
description += f'Remediation: {vulnerability["remediation"]}\n'
description += f'URL: {vulnerability["url"]}\n'

return {'summary': scan_name, 'description': description}

def filter_vulnerabilities(scan_results):
"""
Filter vulnerabilities with medium and high severity to attach.
"""
filtered_vulnerabilities = []

for sublist in scan_results:
for vulnerability in sublist:
if vulnerability["impact"] in IMPACT_LEVELS:
filtered_vulnerabilities.append(vulnerability)

filtered_vulnerabilities_json = json.dumps(filtered_vulnerabilities, indent=4)
filtered_vulnerabilities_bytes = filtered_vulnerabilities_json.encode()

return filtered_vulnerabilities_bytes

def post_request(ticket, scan_results_data):
"""
Post issue request to Jira.
"""
payload = json.dumps({
"fields": {
"project": {
"key": "APS"
},
"summary": ticket['summary'],
"description": ticket['description'],
"issuetype": {
"name": "Story"
},
"customfield_10014": "APS-908",
"priority": {
"id": "10000"
}
}
})

post_url = JIRA_API_URL + '/issue'

response = requests.post(url=post_url, data=payload,
headers=HEADERS, auth=JIRA_AUTH)

print(response.text)

if response.status_code != 201:
print("Error occurred while creating Jira issue:", response.text)
return

# Attach scan results to the Jira issue
issue_key = response.json().get('key')
attach_url = f"{JIRA_API_URL}/issue/{issue_key}/attachments"
headers = {"X-Atlassian-Token": "nocheck"}
filename = scan_name + '.json'
attach_response = requests.post(url=attach_url, files={'file': (filename, scan_results_data)}, headers=headers, auth=JIRA_AUTH)

if attach_response.status_code == 200:
print("Jira issue created and file attached successfully!")
else:
print("Error occurred while attaching file to Jira issue:", attach_response.text)

def main():
with open(ASTRA_SCAN_RESULTS, "r") as file:
scan_results = json.load(file)
vulnerabilities = check_results(scan_results)

if vulnerabilities:
ticket_data = {}
ticket_data = format_ticket(scan_results)
filtered_vulnerabilities = filter_vulnerabilities(scan_results)
post_request(ticket_data, filtered_vulnerabilities)
sys.exit(1)

if __name__ == '__main__':
main()
70 changes: 42 additions & 28 deletions .github/workflows/aps-cypress-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Cypress and Execute Tests
on:
workflow_dispatch: {}
push:
branches: ['test', 'cypress*']
branches: ['test', 'cypress/*']

env:
DASHBOARD_PROJECT_ID: ${{ secrets.CY_DASHBOARD_PRJ_ID }}
Expand All @@ -28,31 +28,6 @@ jobs:
- name: Checkout Portal
uses: actions/checkout@v2

# - name: Determine Download file name
# id: set_variable
# run: |
# echo ${{ runner.arch }}
# if [ "${{ runner.arch }}" == "X64" ]; then
# echo "::set-output name=my_variable::gwa_Linux_x86_64.tgz"
# elif [ "${{ runner.arch }}" == "ARM64" ]; then
# echo "::set-output name=my_variable::gwa_Linux_arm64.tgz"
# else
# echo "unsupported architecture"
# fi

# - name: Download Binary
# uses: robinraju/release-downloader@v1.8
# with:
# repository: "bcgov/gwa-cli"
# latest: true
# fileName: ${{ steps.set_variable.outputs.my_variable }}
# out-file-path: "${{ github.workspace }}/e2e"

# - name: Unzip file
# run: |
# cd ${{ github.workspace }}/e2e
# tar xvzf ${{ steps.set_variable.outputs.my_variable }}

- name: Build Docker Images
run: |
docker compose --profile testsuite build
Expand Down Expand Up @@ -89,15 +64,32 @@ jobs:
name: test-results
path: ${{ github.workspace }}/e2e/results/report

- name: Upload E2E Code Coverage Report
uses: actions/upload-artifact@v2
with:
name: code-coverage
path: ${{ github.workspace }}/e2e/coverage

- name: Instrument the code for coverage analysis
run: |
# Rewrite the paths as the coverage starts with '../app'!
sed -e 's/..\/app/./g' ./e2e/coverage/lcov.info > lcov.info

#cd src
#npm install --legacy-peer-deps
#npx nyc instrument --compact=false . --in-place

- name: SonarCloud Scan
uses: sonarsource/sonarcloud-github-action@master
with:
args: >
-Dsonar.organization=bcgov-oss
-Dsonar.projectKey=aps-portal-e2e
-Dsonar.host.url=https://sonarcloud.io
-Dsonar.sources=src/nextapp
-Dsonar.javascript.lcov.reportPaths=./e2e/coverage/lcov.info
-Dsonar.projectBaseDir=src
-Dsonar.sources=.
-Dsonar.exclusions=nextapp/**,mocks/**,test/**,tools/**,*.json,*.js
-Dsonar.javascript.lcov.reportPaths=/github/workspace/lcov.info
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand All @@ -113,4 +105,26 @@ jobs:
echo -e "Stats: $STATS\n\nFailed Tests:\n$FAILED_TESTS\n\nRun Link: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" > msg
export MSG=$(cat msg)
gh issue create --title "FAILED: Automated Tests($FAILURE_COUNT)" --body "$MSG" --label "automation" --assignee "${{ env.GIT_COMMIT_AUTHOR }}"
exit 1
fi

- name: Set up Python 3.9
if: failure()
uses: actions/setup-python@v2
with:
python-version: '3.9'
architecture: 'x64'

- name: Install Python dependencies
if: failure()
run: |
python -m pip install --upgrade pip
pip install requests

- name: Check Astra results and create Jira issue if necessary
if: failure()
run: python .github/astra-jira.py
env:
JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
JIRA_API_KEY: ${{ secrets.JIRA_API_KEY }}
ASTRA_SCAN_RESULTS: ${{ github.workspace }}/e2e/cypress/fixtures/state/scanResult.json
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ kc.js

# vs code settings
.vscode

e2e/cypress/fixtures/*-plugin.yml
e2e/cypress/fixtures/state/*.pub
e2e/cypress/fixtures/state/*.pem
e2e/cypress/fixtures/state/*.pem
e2e/cypress/fixtures/state/scanResult.json
e2e/cypress/fixtures/state/scanID.json
e2e/cypress/downloads/
67 changes: 30 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,30 @@

[![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/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)
[![Coverage](https://img.shields.io/sonar/coverage/aps-portal-e2e/dev?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge)](https://sonarcloud.io/summary/overall?id=aps-portal-e2e)
![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)


## Introduction


The `API Services Portal` is a frontend for API Providers to manage the lifecycle of their APIs and for Developers to discover and access these APIs. It works in combination with the Kong Community Edition Gateway and Keycloak IAM solution.


## Local Deployment


The repo is setup to create a local deployment of the Portal along with required support services (Postgres, Keycloak, OAuth2-proxy, Feeder and Kong Gateway) using `docker compose`.
The repo is setup to create a local deployment of the Portal along with required support services (Postgres, Keycloak, OAuth2-proxy, Feeder and Kong Gateway) using `docker compose`.

1. Clone and build the [Gateway Admin API](https://github.com/bcgov/gwa-api) (gwa-api)

```
git clone https://github.com/bcgov/gwa-api
cd ./microservices/gatewayApi
docker build -t gwa-api:e2e .
```
```
git clone https://github.com/bcgov/gwa-api
cd ./microservices/gatewayApi
docker build -t gwa-api:e2e .
```

1. Build: Back in `api-services-portal`, run `docker compose build`.
1. Run: `docker compose up`. Wait for startup to complete - look for `Swagger UI registered`.
1. The Portal is now live at http://oauth2proxy.localtest.me:4180
1. To login, use username `janis@idir` and password `awsummer` (or username `local` and password `local`).
1. To login, use username `janis@idir` and password `awsummer` (or username `local` and password `local`).
1. If you have made any changes to the app code, update images by running `docker compose build` then `docker compose up`.
1. Clean up: `docker compose down` removes all the hosted services

Expand Down Expand Up @@ -68,40 +64,39 @@ Use the following configuration to run the Portal locally (outside of Docker) ag

1. Follow [local deployment instructions](#local-deployment) and run `docker compose up`.
1. In `/src` run `npm install`.
1. If using Node version > 17, run `npm install --legacy-peer-deps`

1. If using Node version > 17, run `npm install --legacy-peer-deps`

1. Turn off the docker compose Portal: `docker stop apsportal`
1. Configure the `oauth2-proxy` that is running in Docker:
1. Update `upstreams` in `local/oauth2-proxy/oauth2-proxy-local.cfg` to include the IP address of your local machine, e.g. `upstreams=["http://172.100.100.01:3000"]`
<br>You can obtain the IP address using `hostname -I`.

1. Restart the oauth2-proxy: `docker compose restart oauth2-proxy`
1. Update `DESTINATION_URL` in `local/feeds/.env.local` to include the IP address of your local machine
1. Restart the feeder: `docker compose restart feeder`
1. Update `PORTAL_ACTIVITY_URL` in `local/gwa-api/.env.local` to include the IP address of your local machine
1. Restart the feeder: `docker compose restart gwa-api`


1. Update `upstreams` in `local/oauth2-proxy/oauth2-proxy-local.cfg` to include the IP address of your local machine, e.g. `upstreams=["http://172.100.100.01:3000"]`
<br>You can obtain the IP address using `hostname -I`.

1. Restart the oauth2-proxy: `docker compose restart oauth2-proxy`
1. Update `DESTINATION_URL` in `local/feeds/.env.local` to include the IP address of your local machine
1. Restart the feeder: `docker compose restart feeder`
1. Update `PORTAL_ACTIVITY_URL` in `local/gwa-api/.env.local` to include the IP address of your local machine
1. Restart the feeder: `docker compose restart gwa-api`

1. Start the Portal locally:

```sh
cd src
set -o allexport
source ../.env.local
LOG_LEVEL=debug
KNEX_HOST=kong-db.localtest.me
NEXT_PUBLIC_MOCKS=off
set +o allexport
```sh
cd src
set -o allexport
source ../.env.local
LOG_LEVEL=debug
KNEX_HOST=kong-db.localtest.me
NEXT_PUBLIC_MOCKS=off
set +o allexport

npm run dev
```
npm run dev
```

1. The Portal is now live at http://oauth2proxy.localtest.me:4180 and should auto-update on code changes.


## Design


The `API Services Portal` is a React application using the Chakra UI component library, and using two frameworks: KeystoneJS V5, and NextJS.

The application is divided up into the following six components:
Expand Down Expand Up @@ -176,10 +171,8 @@ Currently support feeders:

Source: `feeds`


## Development


### TypeScript

The client-side Next.js application uses TypeScript, and because it plays nicely with GraphQL types, uses a codegen to generate the API types.
Expand Down
Loading
Loading