From e52954f984b109cc048d36e06d2b5ab950184755 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:20:37 -0700 Subject: [PATCH 001/109] Implement API --- src/api/report_views.py | 32 ++++++++++++++++++++++++++++++++ src/registrar/config/urls.py | 3 +++ 2 files changed, 35 insertions(+) create mode 100644 src/api/report_views.py diff --git a/src/api/report_views.py b/src/api/report_views.py new file mode 100644 index 000000000..2e479a8a1 --- /dev/null +++ b/src/api/report_views.py @@ -0,0 +1,32 @@ +"""Internal API views""" +from django.apps import apps +from django.views.decorators.http import require_http_methods +from django.http import FileResponse, JsonResponse + +import requests + + +from registrar.utility import csv_export +from login_required import login_not_required + +@require_http_methods(["GET"]) +@login_not_required +def get_current_full(request): + # Generate the CSV file + with open("current-full.csv", "w") as file: + csv_export.export_data_full_to_csv(file) + + # Serve the CSV file + response = FileResponse(open('current-full.csv', 'rb')) + return response + +@require_http_methods(["GET"]) +@login_not_required +def get_current_federal(request): + # Generate the CSV file + with open("current-federal.csv", "w") as file: + csv_export.export_data_federal_to_csv(file) + + # Serve the CSV file + response = FileResponse(open('current-federal.csv', 'rb')) + return response diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index c00d1c589..e71f1388e 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -12,6 +12,7 @@ from registrar import views from registrar.views.application import Step from registrar.views.utility import always_404 from api.views import available +from api.report_views import get_current_federal, get_current_full APPLICATION_NAMESPACE = views.ApplicationWizard.URL_NAMESPACE application_urls = [ @@ -73,6 +74,8 @@ urlpatterns = [ path("openid/", include("djangooidc.urls")), path("register/", include((application_urls, APPLICATION_NAMESPACE))), path("api/v1/available/", available, name="available"), + path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"), + path("api/v1/get-report/current-full", get_current_full, name="get-current-full"), path( "todo", lambda r: always_404(r, "We forgot to include this link, sorry."), From bca311b8ca2717d5d5ddce5a6e923ae215daab62 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:28:31 -0700 Subject: [PATCH 002/109] Scripts to generate the file and api update to grab what exists --- .github/workflows/daily-csv-upload.yaml | 49 +++++++++++++++++++ src/api/report_views.py | 32 ------------ src/api/views.py | 25 +++++++++- src/registrar/config/urls.py | 4 +- .../generate_current_federal_report.py | 35 +++++++++++++ .../commands/generate_current_full_report.py | 35 +++++++++++++ 6 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/daily-csv-upload.yaml delete mode 100644 src/api/report_views.py create mode 100644 src/registrar/management/commands/generate_current_federal_report.py create mode 100644 src/registrar/management/commands/generate_current_full_report.py diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml new file mode 100644 index 000000000..729b5bd16 --- /dev/null +++ b/.github/workflows/daily-csv-upload.yaml @@ -0,0 +1,49 @@ +name: Upload current-full.csv and current-federal.csv + +on: + push: + paths-ignore: + - 'docs/**' + - '**.md' + - '.gitignore' + branches: + - rjm + pull_request: + paths-ignore: + - 'docs/**' + - '**.md' + - '.gitignore' + branches: + - rjm + schedule: + # Runs every day at 5 AM UTC + - cron: '0 5 * * *' + +jobs: + upload_reports: + runs-on: ubuntu-latest + if: github.event_name == 'schedule' + steps: + - uses: actions/checkout@v3 + + - name: Install CF CLI + run: | + curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx + sudo mv cf /usr/local/bin + + - name: Login to cloud.gov + run: | + cf api https://api.fr.cloud.gov + cf auth ${{ secrets.CF_USERNAME }} ${{ secrets.CF_PASSWORD }} + cf target -o ${{ secrets.CF_ORG }} -s ${{ secrets.CF_SPACE }} + + - name: Run task + run: cf run-task my-app "/tmp/lifecycle/shell -c './manage.py generate_current_full_and_federal_reports.py'" + + - name: Commit and push CSV files + run: | + git config --global user.name 'GitHub Actions' + git config --global user.email 'actions@github.com' + git add current-full.csv current-federal.csv + git commit -m "Update CSV files" + git push \ No newline at end of file diff --git a/src/api/report_views.py b/src/api/report_views.py deleted file mode 100644 index 2e479a8a1..000000000 --- a/src/api/report_views.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Internal API views""" -from django.apps import apps -from django.views.decorators.http import require_http_methods -from django.http import FileResponse, JsonResponse - -import requests - - -from registrar.utility import csv_export -from login_required import login_not_required - -@require_http_methods(["GET"]) -@login_not_required -def get_current_full(request): - # Generate the CSV file - with open("current-full.csv", "w") as file: - csv_export.export_data_full_to_csv(file) - - # Serve the CSV file - response = FileResponse(open('current-full.csv', 'rb')) - return response - -@require_http_methods(["GET"]) -@login_not_required -def get_current_federal(request): - # Generate the CSV file - with open("current-federal.csv", "w") as file: - csv_export.export_data_federal_to_csv(file) - - # Serve the CSV file - response = FileResponse(open('current-federal.csv', 'rb')) - return response diff --git a/src/api/views.py b/src/api/views.py index 2cb23a9b2..1ed8a0888 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,7 +1,7 @@ """Internal API views""" from django.apps import apps from django.views.decorators.http import require_http_methods -from django.http import JsonResponse +from django.http import FileResponse, HttpResponse, JsonResponse import requests @@ -89,3 +89,26 @@ def available(request, domain=""): return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["unavailable"]}) except Exception: return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["error"]}) + +@require_http_methods(["GET"]) +@login_not_required +def get_current_full(request): + # Open the CSV file + file_path = './migrationData/current-full.csv' + return serve_file(file_path) + +@require_http_methods(["GET"]) +@login_not_required +def get_current_federal(request): + # Open the CSV file + file_path = './migrationData/current-federal.csv' + return serve_file(file_path) + +def serve_file(file_path): + """Downloads a file based on a given filepath. Returns a 404 if not found.""" + if os.path.exists(file_path): + # Serve the CSV file + response = FileResponse(open(file_path, 'rb')) + return response + else: + return HttpResponse("File not found", status=404) \ No newline at end of file diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index e71f1388e..6ded44913 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -11,8 +11,8 @@ from django.views.generic import RedirectView from registrar import views from registrar.views.application import Step from registrar.views.utility import always_404 -from api.views import available -from api.report_views import get_current_federal, get_current_full +from api.views import available, get_current_federal, get_current_full + APPLICATION_NAMESPACE = views.ApplicationWizard.URL_NAMESPACE application_urls = [ diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py new file mode 100644 index 000000000..f07a35c65 --- /dev/null +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -0,0 +1,35 @@ +"""Generates current-full.csv and current-federal.csv then uploads them to the desired URL.""" +import glob +import logging + +import os +import shutil + +from django.core.management import BaseCommand + +from registrar.management.commands.utility.terminal_helper import TerminalHelper +from registrar.utility import csv_export + + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "" + + def add_arguments(self, parser): + """Add our two filename arguments.""" + parser.add_argument("--directory", default="migrationdata", help="Desired directory") + + def handle(self, **options): + # Ensures a slash is added + directory = os.path.join(options.get("directory"), "") + logger.info("Generating report...") + self.generate_current_federal_report(directory) + logger.info(f"Success! Created {directory}current-federal.csv") + + def generate_current_federal_report(self, directory): + """Creates a current-full.csv file under the migrationdata/ directory""" + file_path = os.path.join(directory, "current-federal.csv") + with open(file_path, "w") as file: + csv_export.export_data_federal_to_csv(file) diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py new file mode 100644 index 000000000..49f2127a8 --- /dev/null +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -0,0 +1,35 @@ +"""Generates current-full.csv and current-federal.csv then uploads them to the desired URL.""" +import glob +import logging + +import os +import shutil + +from django.core.management import BaseCommand + +from registrar.management.commands.utility.terminal_helper import TerminalHelper +from registrar.utility import csv_export + + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "" + + def add_arguments(self, parser): + """Add our two filename arguments.""" + parser.add_argument("--directory", default="migrationdata", help="Desired directory") + + def handle(self, **options): + # Ensures a slash is added + directory = os.path.join(options.get("directory"), "") + logger.info("Generating report...") + self.generate_current_full_report(directory) + logger.info(f"Success! Created {directory}current-full.csv") + + def generate_current_full_report(self, directory): + """Creates a current-full.csv file under the migrationdata/ directory""" + file_path = os.path.join(directory, "current-full.csv") + with open(file_path, "w") as file: + csv_export.export_data_full_to_csv(file) From d52359d1c58ea1f37efaf7b4425296b5a9c9831e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:45:23 -0700 Subject: [PATCH 003/109] Yaml stub --- .github/workflows/daily-csv-upload.yaml | 39 ++++++------------------- src/api/views.py | 1 + 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 729b5bd16..3863c36c4 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -1,28 +1,17 @@ name: Upload current-full.csv and current-federal.csv on: - push: - paths-ignore: - - 'docs/**' - - '**.md' - - '.gitignore' - branches: - - rjm - pull_request: - paths-ignore: - - 'docs/**' - - '**.md' - - '.gitignore' - branches: - - rjm schedule: # Runs every day at 5 AM UTC - cron: '0 5 * * *' + jobs: upload_reports: runs-on: ubuntu-latest - if: github.event_name == 'schedule' + env: + CF_USERNAME: CF_${{ github.event.inputs.environment }}_USERNAME + CF_PASSWORD: CF_${{ github.event.inputs.environment }}_PASSWORD steps: - uses: actions/checkout@v3 @@ -31,19 +20,9 @@ jobs: curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx sudo mv cf /usr/local/bin - - name: Login to cloud.gov - run: | - cf api https://api.fr.cloud.gov - cf auth ${{ secrets.CF_USERNAME }} ${{ secrets.CF_PASSWORD }} - cf target -o ${{ secrets.CF_ORG }} -s ${{ secrets.CF_SPACE }} - - - name: Run task - run: cf run-task my-app "/tmp/lifecycle/shell -c './manage.py generate_current_full_and_federal_reports.py'" + - name: Generate current-federal.csv + run: cf run-task getgov-za "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" + - - name: Commit and push CSV files - run: | - git config --global user.name 'GitHub Actions' - git config --global user.email 'actions@github.com' - git add current-full.csv current-federal.csv - git commit -m "Update CSV files" - git push \ No newline at end of file + - name: Generate current-full.csv + run: cf run-task getgov-za "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report.py'" diff --git a/src/api/views.py b/src/api/views.py index 1ed8a0888..9653a906e 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,4 +1,5 @@ """Internal API views""" +import os from django.apps import apps from django.views.decorators.http import require_http_methods from django.http import FileResponse, HttpResponse, JsonResponse From d405d4ca7927bdff44ed46ad37b3e324814d3395 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:16:34 -0700 Subject: [PATCH 004/109] Test yaml --- .github/workflows/daily-csv-upload.yaml | 64 +++++++++++++++++++++---- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 3863c36c4..daf1fff19 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -1,28 +1,72 @@ name: Upload current-full.csv and current-federal.csv +run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: schedule: # Runs every day at 5 AM UTC - - cron: '0 5 * * *' - + # TODO: - cron: '0 5 * * *' + - cron: '*/5 * * * *' jobs: - upload_reports: + variables: + if: | + startsWith(github.head_ref, 'ab/') + || startsWith(github.head_ref, 'bl/') + || startsWith(github.head_ref, 'rjm/') + || startsWith(github.head_ref, 'rb/') + || startsWith(github.head_ref, 'ko/') + || startsWith(github.head_ref, 'gd/') + || startsWith(github.head_ref, 'za/') + || startsWith(github.head_ref, 'rh/') + || startsWith(github.head_ref, 'nl/') + || startsWith(github.head_ref, 'dk/') + || startsWith(github.head_ref, 'es/') + || startsWith(github.head_ref, 'ky/') + outputs: + environment: ${{ steps.var.outputs.environment}} + runs-on: "ubuntu-latest" + steps: + - name: Setting global variables + uses: actions/github-script@v6 + id: var + with: + script: | + core.setOutput('environment', '${{ github.head_ref }}'.split("/")[0]); + deploy: runs-on: ubuntu-latest - env: - CF_USERNAME: CF_${{ github.event.inputs.environment }}_USERNAME - CF_PASSWORD: CF_${{ github.event.inputs.environment }}_PASSWORD + needs: [variables] steps: - uses: actions/checkout@v3 - - - name: Install CF CLI + - name: Install CF CLI run: | curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx sudo mv cf /usr/local/bin - name: Generate current-federal.csv - run: cf run-task getgov-za "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" + uses: 18f/cg-deploy-action@main + with: + cf_username: ${{ secrets[env.CF_USERNAME] }} + cf_password: ${{ secrets[env.CF_PASSWORD] }} + cf_org: cisa-dotgov + cf_space: ${{ github.event.inputs.environment }} + full_command: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" - - name: Generate current-full.csv run: cf run-task getgov-za "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report.py'" + + comment: + runs-on: ubuntu-latest + needs: [variables, deploy] + steps: + - uses: actions/github-script@v6 + env: + ENVIRONMENT: ${{ needs.variables.outputs.environment }} + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '🥳 Successfully uploaded CSVs to **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.' + }) From 9a872d200587fdda48eed383efb98429dd4a9888 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:23:07 -0700 Subject: [PATCH 005/109] Fix syntax error --- .github/workflows/daily-csv-upload.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index daf1fff19..68cbb53eb 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -37,7 +37,8 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - - name: Install CF CLI + + - name: Install CF CLI run: | curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx sudo mv cf /usr/local/bin From 45542f12f6208c0e79c6c74b140b2b2e390820a3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:36:53 -0700 Subject: [PATCH 006/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 68cbb53eb..9dc185ea8 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,10 +2,12 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: + pull_request: + schedule: # Runs every day at 5 AM UTC # TODO: - cron: '0 5 * * *' - - cron: '*/5 * * * *' + - cron: '*/2 * * * *' jobs: variables: @@ -37,7 +39,7 @@ jobs: needs: [variables] steps: - uses: actions/checkout@v3 - + - name: Install CF CLI run: | curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx From e2473f337d16776073c3541e95e4bcf5282694d3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:42:24 -0700 Subject: [PATCH 007/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 9dc185ea8..5e5226d15 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -47,11 +47,16 @@ jobs: - name: Generate current-federal.csv uses: 18f/cg-deploy-action@main + env: + DEPLOY_NOW: thanks + ENVIRONMENT: ${{ needs.variables.outputs.environment }} + CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME + CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD with: cf_username: ${{ secrets[env.CF_USERNAME] }} cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov - cf_space: ${{ github.event.inputs.environment }} + cf_space: ${{ env.ENVIRONMENT }} full_command: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" - name: Generate current-full.csv From 61cb536ef800bda19850d14342cac396485c5db7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:51:08 -0700 Subject: [PATCH 008/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 5e5226d15..0b61a0339 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -34,7 +34,20 @@ jobs: with: script: | core.setOutput('environment', '${{ github.head_ref }}'.split("/")[0]); - deploy: + wait_for_deploy: + runs-on: ubuntu-latest + steps: + - name: Wait for deploy to complete + uses: fountainhead/action-wait-for-check@v1.0.0 + id: wait-for-deploy + with: + token: ${{ secrets.GITHUB_TOKEN }} + checkName: "deploy" # replace with the name of the deploy job + ref: ${{ github.event.pull_request.head.sha }} # the commit SHA of the head commit of the PR + timeoutSeconds: 600 # the maximum time to wait for the check to complete, in seconds + intervalSeconds: 10 # the time to wait between checks, in seconds + + upload_reports: runs-on: ubuntu-latest needs: [variables] steps: @@ -57,10 +70,10 @@ jobs: cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov cf_space: ${{ env.ENVIRONMENT }} - full_command: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" + run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" - name: Generate current-full.csv - run: cf run-task getgov-za "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report.py'" + run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report.py'" comment: runs-on: ubuntu-latest From 161e2583cb8d657d977ac3d611558a92335f29e9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:53:30 -0700 Subject: [PATCH 009/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 0b61a0339..60f75cb5b 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -59,17 +59,6 @@ jobs: sudo mv cf /usr/local/bin - name: Generate current-federal.csv - uses: 18f/cg-deploy-action@main - env: - DEPLOY_NOW: thanks - ENVIRONMENT: ${{ needs.variables.outputs.environment }} - CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME - CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - with: - cf_username: ${{ secrets[env.CF_USERNAME] }} - cf_password: ${{ secrets[env.CF_PASSWORD] }} - cf_org: cisa-dotgov - cf_space: ${{ env.ENVIRONMENT }} run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" - name: Generate current-full.csv From 95273284cc0bee7bfd7f110a13a427ad20f6803f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:00:40 -0700 Subject: [PATCH 010/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 60f75cb5b..99eff3ee0 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -66,7 +66,7 @@ jobs: comment: runs-on: ubuntu-latest - needs: [variables, deploy] + needs: [variables, upload_reports] steps: - uses: actions/github-script@v6 env: From d14da1460f00794772f56aed1107db86be2de3c6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:06:49 -0700 Subject: [PATCH 011/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 99eff3ee0..1036c251c 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -49,7 +49,7 @@ jobs: upload_reports: runs-on: ubuntu-latest - needs: [variables] + needs: [variables, wait_for_deploy] steps: - uses: actions/checkout@v3 @@ -58,6 +58,19 @@ jobs: curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx sudo mv cf /usr/local/bin + - name: Set login credentials + uses: 18f/cg-deploy-action@main + env: + DEPLOY_NOW: thanks + ENVIRONMENT: ${{ needs.variables.outputs.environment }} + CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME + CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD + with: + cf_username: ${{ secrets[env.CF_USERNAME] }} + cf_password: ${{ secrets[env.CF_PASSWORD] }} + cf_org: cisa-dotgov + cf_space: ${{ env.ENVIRONMENT }} + - name: Generate current-federal.csv run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" From 328e1fca956168b54939000855b394238b39cb6b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:10:48 -0700 Subject: [PATCH 012/109] Remove deploy now --- .github/workflows/daily-csv-upload.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 1036c251c..c73437cca 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -61,7 +61,6 @@ jobs: - name: Set login credentials uses: 18f/cg-deploy-action@main env: - DEPLOY_NOW: thanks ENVIRONMENT: ${{ needs.variables.outputs.environment }} CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD From dffca3b5879cac832038e4d090e537eef08e2cb2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:08:30 -0700 Subject: [PATCH 013/109] Test --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index c73437cca..e5af53f05 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -34,6 +34,7 @@ jobs: with: script: | core.setOutput('environment', '${{ github.head_ref }}'.split("/")[0]); + wait_for_deploy: runs-on: ubuntu-latest steps: @@ -59,7 +60,6 @@ jobs: sudo mv cf /usr/local/bin - name: Set login credentials - uses: 18f/cg-deploy-action@main env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME From 06835dbf44b2fcca05277dc4de6e2003e2272817 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:14:16 -0700 Subject: [PATCH 014/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index e5af53f05..4137b314c 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -60,6 +60,7 @@ jobs: sudo mv cf /usr/local/bin - name: Set login credentials + uses: 18f/cg-deploy-action@main env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME From 862339cd32a2ae99bb03cf25d89ec1167c562bff Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:24:32 -0700 Subject: [PATCH 015/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 4137b314c..370fd6ac1 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -60,16 +60,14 @@ jobs: sudo mv cf /usr/local/bin - name: Set login credentials - uses: 18f/cg-deploy-action@main + run: | + cf api https://api.fr.cloud.gov + cf auth ${{ secrets[env.CF_USERNAME] }} ${{ secrets[env.CF_PASSWORD] }} + cf target -o cisa-dotgov -s ${{ env.ENVIRONMENT }} env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - with: - cf_username: ${{ secrets[env.CF_USERNAME] }} - cf_password: ${{ secrets[env.CF_PASSWORD] }} - cf_org: cisa-dotgov - cf_space: ${{ env.ENVIRONMENT }} - name: Generate current-federal.csv run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" From 91cdd44d7f92c4260dd7e8e27b330b260627f90c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:30:24 -0700 Subject: [PATCH 016/109] Upload files --- .github/workflows/daily-csv-upload.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 370fd6ac1..ef7e1e669 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -71,10 +71,14 @@ jobs: - name: Generate current-federal.csv run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" - + env: + ENVIRONMENT: ${{ needs.variables.outputs.environment }} + - name: Generate current-full.csv run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report.py'" - + env: + ENVIRONMENT: ${{ needs.variables.outputs.environment }} + comment: runs-on: ubuntu-latest needs: [variables, upload_reports] From ff4773f8c71ab5ee1397511629e28603d4928ed9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:51:03 -0700 Subject: [PATCH 017/109] Test command --- .github/workflows/daily-csv-upload.yaml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index ef7e1e669..2564d7188 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -43,10 +43,12 @@ jobs: id: wait-for-deploy with: token: ${{ secrets.GITHUB_TOKEN }} - checkName: "deploy" # replace with the name of the deploy job - ref: ${{ github.event.pull_request.head.sha }} # the commit SHA of the head commit of the PR - timeoutSeconds: 600 # the maximum time to wait for the check to complete, in seconds - intervalSeconds: 10 # the time to wait between checks, in seconds + checkName: "deploy" + ref: ${{ github.event.pull_request.head.sha }} + # the maximum time to wait for the check to complete, in seconds + timeoutSeconds: 600 + # the time to wait between checks, in seconds + intervalSeconds: 10 upload_reports: runs-on: ubuntu-latest @@ -70,7 +72,7 @@ jobs: CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - name: Generate current-federal.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report.py'" + run: cf run-task getgov-${{ env.ENVIRONMENT }} --wait --command "python manage.py generate_current_full_report.py --name current_full" env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} From 8e9dc8219028584a30a071c325a5a5cab63df3f2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:00:00 -0700 Subject: [PATCH 018/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 2564d7188..9c9bfacd8 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -72,7 +72,7 @@ jobs: CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - name: Generate current-federal.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} --wait --command "python manage.py generate_current_full_report.py --name current_full" + run: cf run-task getgov-${{ env.ENVIRONMENT }} "python manage.py generate_current_full_report.py" env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} From c070651a8de2181eac4d5b95ec34122d5839ad66 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:12:08 -0700 Subject: [PATCH 019/109] Fix incorrect paths --- .github/workflows/daily-csv-upload.yaml | 4 ++-- src/api/views.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 9c9bfacd8..d4cc0a700 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -72,12 +72,12 @@ jobs: CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - name: Generate current-federal.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} "python manage.py generate_current_full_report.py" + run: cf run-task getgov-${{ env.ENVIRONMENT }} "python manage.py generate_current_full_report" env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} - name: Generate current-full.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report.py'" + run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report'" env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} diff --git a/src/api/views.py b/src/api/views.py index 9653a906e..594ba3f22 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -95,14 +95,14 @@ def available(request, domain=""): @login_not_required def get_current_full(request): # Open the CSV file - file_path = './migrationData/current-full.csv' + file_path = '../migrationdata/current-full.csv' return serve_file(file_path) @require_http_methods(["GET"]) @login_not_required def get_current_federal(request): # Open the CSV file - file_path = './migrationData/current-federal.csv' + file_path = '../migrationdata/current-federal.csv' return serve_file(file_path) def serve_file(file_path): From 7772a13834a4ea508daa174a0001d73f23cd63ea Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:45:52 -0700 Subject: [PATCH 020/109] Fix broken api --- src/api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 594ba3f22..298e5fb8c 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -95,14 +95,14 @@ def available(request, domain=""): @login_not_required def get_current_full(request): # Open the CSV file - file_path = '../migrationdata/current-full.csv' + file_path = 'migrationdata/current-full.csv' return serve_file(file_path) @require_http_methods(["GET"]) @login_not_required def get_current_federal(request): # Open the CSV file - file_path = '../migrationdata/current-federal.csv' + file_path = 'migrationdata/current-federal.csv' return serve_file(file_path) def serve_file(file_path): From a97d891b7d28d3eb12eceaf6fd3e1546e666d1a6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:48:24 -0700 Subject: [PATCH 021/109] Test cron schedule --- .github/workflows/daily-csv-upload.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index d4cc0a700..0a408073c 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,12 +2,10 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: - pull_request: - schedule: # Runs every day at 5 AM UTC # TODO: - cron: '0 5 * * *' - - cron: '*/2 * * * *' + - cron: '*/1 * * * *' jobs: variables: From f050618872ccdb99d6682c59a0b21ef3206cac5c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:53:25 -0700 Subject: [PATCH 022/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 0a408073c..d96012564 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -4,8 +4,7 @@ run-name: Upload current-full.csv and current-federal.csv for branch ${{ github. on: schedule: # Runs every day at 5 AM UTC - # TODO: - cron: '0 5 * * *' - - cron: '*/1 * * * *' + cron: '0 5 * * *' jobs: variables: @@ -93,5 +92,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: '🥳 Successfully uploaded CSVs to **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.' + body: '🥳 Successfully uploaded current-full.csv and current-federal.csv to **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.' }) From 1d70c5058a2520cc3f4473c83b7193f36f83b533 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:54:05 -0700 Subject: [PATCH 023/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index d96012564..76624fac6 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -4,7 +4,7 @@ run-name: Upload current-full.csv and current-federal.csv for branch ${{ github. on: schedule: # Runs every day at 5 AM UTC - cron: '0 5 * * *' + - cron: '0 5 * * *' jobs: variables: From bcf5c6bc5b733b695ff5ee331e46235d27440fb6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:00:06 -0700 Subject: [PATCH 024/109] Linting + fix tests --- src/api/views.py | 11 +++++++---- src/registrar/tests/test_url_auth.py | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 298e5fb8c..a565504df 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -91,25 +91,28 @@ def available(request, domain=""): except Exception: return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["error"]}) + @require_http_methods(["GET"]) @login_not_required def get_current_full(request): # Open the CSV file - file_path = 'migrationdata/current-full.csv' + file_path = "migrationdata/current-full.csv" return serve_file(file_path) + @require_http_methods(["GET"]) @login_not_required def get_current_federal(request): # Open the CSV file - file_path = 'migrationdata/current-federal.csv' + file_path = "migrationdata/current-federal.csv" return serve_file(file_path) + def serve_file(file_path): """Downloads a file based on a given filepath. Returns a 404 if not found.""" if os.path.exists(file_path): # Serve the CSV file - response = FileResponse(open(file_path, 'rb')) + response = FileResponse(open(file_path, "rb")) return response else: - return HttpResponse("File not found", status=404) \ No newline at end of file + return HttpResponse("File not found", status=404) diff --git a/src/registrar/tests/test_url_auth.py b/src/registrar/tests/test_url_auth.py index fe3116147..b1a8b8dc7 100644 --- a/src/registrar/tests/test_url_auth.py +++ b/src/registrar/tests/test_url_auth.py @@ -111,6 +111,8 @@ class TestURLAuth(TestCase): "/openid/callback/login/", "/openid/callback/logout/", "/api/v1/available/whitehouse.gov", + "/api/v1/get-report/current-federal", + "/api/v1/get-report/current-full" ] def assertURLIsProtectedByAuth(self, url): From d0d40baff5c86a43fa05b8c180be8ff20728de4e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:04:38 -0700 Subject: [PATCH 025/109] Linting --- .../management/commands/generate_current_federal_report.py | 5 ----- .../management/commands/generate_current_full_report.py | 5 ----- src/registrar/tests/test_url_auth.py | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index f07a35c65..83f76f1cc 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -1,13 +1,8 @@ """Generates current-full.csv and current-federal.csv then uploads them to the desired URL.""" -import glob import logging - import os -import shutil from django.core.management import BaseCommand - -from registrar.management.commands.utility.terminal_helper import TerminalHelper from registrar.utility import csv_export diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index 49f2127a8..92519cdc1 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -1,13 +1,8 @@ """Generates current-full.csv and current-federal.csv then uploads them to the desired URL.""" -import glob import logging - import os -import shutil from django.core.management import BaseCommand - -from registrar.management.commands.utility.terminal_helper import TerminalHelper from registrar.utility import csv_export diff --git a/src/registrar/tests/test_url_auth.py b/src/registrar/tests/test_url_auth.py index b1a8b8dc7..f131253f6 100644 --- a/src/registrar/tests/test_url_auth.py +++ b/src/registrar/tests/test_url_auth.py @@ -112,7 +112,7 @@ class TestURLAuth(TestCase): "/openid/callback/logout/", "/api/v1/available/whitehouse.gov", "/api/v1/get-report/current-federal", - "/api/v1/get-report/current-full" + "/api/v1/get-report/current-full", ] def assertURLIsProtectedByAuth(self, url): From 01b5d46a45614c7f38a9ff2b083e89aec1a49489 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 08:58:39 -0700 Subject: [PATCH 026/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 76624fac6..090561c77 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -4,7 +4,8 @@ run-name: Upload current-full.csv and current-federal.csv for branch ${{ github. on: schedule: # Runs every day at 5 AM UTC - - cron: '0 5 * * *' + # - cron: '0 5 * * *' + - cron: "* * * * *" jobs: variables: @@ -32,7 +33,7 @@ jobs: script: | core.setOutput('environment', '${{ github.head_ref }}'.split("/")[0]); - wait_for_deploy: + "Wait for deploy": runs-on: ubuntu-latest steps: - name: Wait for deploy to complete @@ -47,9 +48,9 @@ jobs: # the time to wait between checks, in seconds intervalSeconds: 10 - upload_reports: + "Upload reports": runs-on: ubuntu-latest - needs: [variables, wait_for_deploy] + needs: [variables, "Wait for deploy"] steps: - uses: actions/checkout@v3 @@ -80,7 +81,7 @@ jobs: comment: runs-on: ubuntu-latest - needs: [variables, upload_reports] + needs: [variables, "Upload reports"] steps: - uses: actions/github-script@v6 env: From 591ee822288e72e3a3d731baf3db53e3c2a75083 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 09:11:46 -0700 Subject: [PATCH 027/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 34 +++++++------------------ 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 090561c77..ae0c9d6ee 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,36 +2,20 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: + push: + paths-ignore: + - 'docs/**' + - '**.md' + - '.gitignore' + branches: + - za schedule: # Runs every day at 5 AM UTC - # - cron: '0 5 * * *' - - cron: "* * * * *" + - cron: "0 5 * * *" jobs: variables: - if: | - startsWith(github.head_ref, 'ab/') - || startsWith(github.head_ref, 'bl/') - || startsWith(github.head_ref, 'rjm/') - || startsWith(github.head_ref, 'rb/') - || startsWith(github.head_ref, 'ko/') - || startsWith(github.head_ref, 'gd/') - || startsWith(github.head_ref, 'za/') - || startsWith(github.head_ref, 'rh/') - || startsWith(github.head_ref, 'nl/') - || startsWith(github.head_ref, 'dk/') - || startsWith(github.head_ref, 'es/') - || startsWith(github.head_ref, 'ky/') - outputs: - environment: ${{ steps.var.outputs.environment}} - runs-on: "ubuntu-latest" - steps: - - name: Setting global variables - uses: actions/github-script@v6 - id: var - with: - script: | - core.setOutput('environment', '${{ github.head_ref }}'.split("/")[0]); + environment: "za" "Wait for deploy": runs-on: ubuntu-latest From 3db4fcfb41e6006cb57d064fbdf934321fd4dfd0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 09:36:25 -0700 Subject: [PATCH 028/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index ae0c9d6ee..36ca423d8 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -10,7 +10,7 @@ on: branches: - za schedule: - # Runs every day at 5 AM UTC + # Runs every day at 5 AM UTC. - cron: "0 5 * * *" jobs: From f7e6eb7e47fe98b81dcb2298021d372e812219e7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 09:37:31 -0700 Subject: [PATCH 029/109] Temp pull request for testing --- .github/workflows/daily-csv-upload.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 36ca423d8..4d842763a 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,13 +2,7 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: - push: - paths-ignore: - - 'docs/**' - - '**.md' - - '.gitignore' - branches: - - za + pull_request: schedule: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" From 6671aa2cba357b36f5ea241a62479e6fa36dc77e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 09:54:41 -0700 Subject: [PATCH 030/109] Test workflow --- .github/workflows/daily-csv-upload.yaml | 34 +++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 4d842763a..1166ff131 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,14 +2,44 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: - pull_request: + workflow_dispatch: + inputs: + environment: + type: choice + description: Which environment load reports for? + options: + - stable + - staging + - development + - ky + - es + - nl + - rh + - za + - gd + - rb + - ko + - ab + - bl + - rjm + - dk schedule: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" jobs: variables: - environment: "za" + outputs: + environment: ${{ steps.var.outputs.environment }} + runs-on: "ubuntu-latest" + steps: + - name: Setting global variables + uses: actions/github-script@v6 + id: var + with: + script: | + const environment = context.event.inputs.environment || 'za'; + core.setOutput('environment', environment); "Wait for deploy": runs-on: ubuntu-latest From d4c91a824dd898ae5392e60d96f4c968d8bb20b3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:04:29 -0700 Subject: [PATCH 031/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 1166ff131..abc710795 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,6 +2,7 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: + pull_request: workflow_dispatch: inputs: environment: From 579f7e9c17aed744daf17694ce1326c2688d32bd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:12:20 -0700 Subject: [PATCH 032/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index abc710795..28934176c 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -42,7 +42,7 @@ jobs: const environment = context.event.inputs.environment || 'za'; core.setOutput('environment', environment); - "Wait for deploy": + wait-for-deploy: runs-on: ubuntu-latest steps: - name: Wait for deploy to complete @@ -57,9 +57,9 @@ jobs: # the time to wait between checks, in seconds intervalSeconds: 10 - "Upload reports": + upload-reports: runs-on: ubuntu-latest - needs: [variables, "Wait for deploy"] + needs: [variables, wait-for-deploy] steps: - uses: actions/checkout@v3 @@ -90,7 +90,7 @@ jobs: comment: runs-on: ubuntu-latest - needs: [variables, "Upload reports"] + needs: [variables, upload-reports] steps: - uses: actions/github-script@v6 env: From 963aa8cbc8fb983de0cb2ab161c091c7778fc3e0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:37:59 -0700 Subject: [PATCH 033/109] Check if dispatch wasnt triggered --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 28934176c..8a804cde7 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,7 +39,7 @@ jobs: id: var with: script: | - const environment = context.event.inputs.environment || 'za'; + const environment = context.event.inputs ? context.event.inputs.environment : 'za'; core.setOutput('environment', environment); wait-for-deploy: From 95d0168d7ede893abd1b84fb4c0c859e32a49223 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:46:56 -0700 Subject: [PATCH 034/109] Fix invalid environment call --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 8a804cde7..a4261300f 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,7 +39,7 @@ jobs: id: var with: script: | - const environment = context.event.inputs ? context.event.inputs.environment : 'za'; + const environment = github.event.inputs ? github.event.inputs.environment : 'za'; core.setOutput('environment', environment); wait-for-deploy: From 2a8d1cadbdaef5fc407339c5db1a118a73883827 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:52:05 -0700 Subject: [PATCH 035/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index a4261300f..908223946 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,7 +39,7 @@ jobs: id: var with: script: | - const environment = github.event.inputs ? github.event.inputs.environment : 'za'; + const environment = (github and github.event and github.event.inputs) ? github.event.inputs.environment : 'za'; core.setOutput('environment', environment); wait-for-deploy: From c66ef110ae600ce44322197d1a83f8c90368e0f4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:53:28 -0700 Subject: [PATCH 036/109] Fix and operator --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 908223946..19ec6d906 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,7 +39,7 @@ jobs: id: var with: script: | - const environment = (github and github.event and github.event.inputs) ? github.event.inputs.environment : 'za'; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; core.setOutput('environment', environment); wait-for-deploy: From a4f4ac754847c6ca1ff25374045671b5741c533a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:13:22 -0700 Subject: [PATCH 037/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 19ec6d906..2484d7015 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -7,7 +7,7 @@ on: inputs: environment: type: choice - description: Which environment load reports for? + description: Which environment do you wish load reports for? options: - stable - staging From 74c2a86e57516a246a38a09f89b5ef08d0631b9f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:30:12 -0700 Subject: [PATCH 038/109] Test update federal --- .github/workflows/daily-csv-upload.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 2484d7015..8b59f2468 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -79,12 +79,15 @@ jobs: CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - name: Generate current-federal.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} "python manage.py generate_current_full_report" + run: | + cf ssh getgov-${{ env.ENVIRONMENT }} + /tmp/lifecycle/shell + ./manage.py generate_current_federal_report env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} - name: Generate current-full.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report'" + run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report'" env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} From 1ca86bc72df85cbea5bcdae86351d11d55242def Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:06:36 -0700 Subject: [PATCH 039/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 8b59f2468..a90d4c55d 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -68,23 +68,18 @@ jobs: curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx sudo mv cf /usr/local/bin - - name: Set login credentials + - name: Generate current-federal.csv run: | cf api https://api.fr.cloud.gov cf auth ${{ secrets[env.CF_USERNAME] }} ${{ secrets[env.CF_PASSWORD] }} cf target -o cisa-dotgov -s ${{ env.ENVIRONMENT }} - env: - ENVIRONMENT: ${{ needs.variables.outputs.environment }} - CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME - CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - - - name: Generate current-federal.csv - run: | cf ssh getgov-${{ env.ENVIRONMENT }} /tmp/lifecycle/shell ./manage.py generate_current_federal_report env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} + CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME + CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - name: Generate current-full.csv run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report'" From 9c05dcde44ba3a88d770f4a0d9df92b2fcad207c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:23:43 -0700 Subject: [PATCH 040/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index a90d4c55d..5a1562586 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -73,9 +73,7 @@ jobs: cf api https://api.fr.cloud.gov cf auth ${{ secrets[env.CF_USERNAME] }} ${{ secrets[env.CF_PASSWORD] }} cf target -o cisa-dotgov -s ${{ env.ENVIRONMENT }} - cf ssh getgov-${{ env.ENVIRONMENT }} - /tmp/lifecycle/shell - ./manage.py generate_current_federal_report + cf ssh getgov-${{ env.ENVIRONMENT }} -c "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report'" env: ENVIRONMENT: ${{ needs.variables.outputs.environment }} CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME From 90282c7cdcc802f10588d36b2b984e9614bca898 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:44:18 -0700 Subject: [PATCH 041/109] Another approach --- .github/workflows/daily-csv-upload.yaml | 40 ++++++++++++------------- src/api/views.py | 2 -- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 5a1562586..995bb88fd 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -7,7 +7,7 @@ on: inputs: environment: type: choice - description: Which environment do you wish load reports for? + description: Which environment load reports for? options: - stable - staging @@ -59,31 +59,29 @@ jobs: upload-reports: runs-on: ubuntu-latest - needs: [variables, wait-for-deploy] + env: + CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME + CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: - - uses: actions/checkout@v3 - - - name: Install CF CLI - run: | - curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx - sudo mv cf /usr/local/bin - name: Generate current-federal.csv - run: | - cf api https://api.fr.cloud.gov - cf auth ${{ secrets[env.CF_USERNAME] }} ${{ secrets[env.CF_PASSWORD] }} - cf target -o cisa-dotgov -s ${{ env.ENVIRONMENT }} - cf ssh getgov-${{ env.ENVIRONMENT }} -c "/tmp/lifecycle/shell -c './manage.py generate_current_federal_report'" - env: - ENVIRONMENT: ${{ needs.variables.outputs.environment }} - CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME - CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD + uses: 18f/cg-deploy-action@main + with: + cf_username: ${{ secrets[env.CF_USERNAME] }} + cf_password: ${{ secrets[env.CF_PASSWORD] }} + cf_org: cisa-dotgov + cf_space: ${{ needs.variables.outputs.environment }} + full_command: "cf run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" - name: Generate current-full.csv - run: cf run-task getgov-${{ env.ENVIRONMENT }} "/tmp/lifecycle/shell -c './manage.py generate_current_full_report'" - env: - ENVIRONMENT: ${{ needs.variables.outputs.environment }} - + uses: 18f/cg-deploy-action@main + with: + cf_username: ${{ secrets[env.CF_USERNAME] }} + cf_password: ${{ secrets[env.CF_PASSWORD] }} + cf_org: cisa-dotgov + cf_space: ${{ needs.variables.outputs.environment }} + full_command: "cf run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_full_report' --name full" + comment: runs-on: ubuntu-latest needs: [variables, upload-reports] diff --git a/src/api/views.py b/src/api/views.py index a565504df..c624ee73c 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -3,7 +3,6 @@ import os from django.apps import apps from django.views.decorators.http import require_http_methods from django.http import FileResponse, HttpResponse, JsonResponse - import requests from login_required import login_not_required @@ -107,7 +106,6 @@ def get_current_federal(request): file_path = "migrationdata/current-federal.csv" return serve_file(file_path) - def serve_file(file_path): """Downloads a file based on a given filepath. Returns a 404 if not found.""" if os.path.exists(file_path): From c64e7d67cba0540291b4615eaf6eb307cd160411 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:47:54 -0700 Subject: [PATCH 042/109] Update needs --- .github/workflows/daily-csv-upload.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 995bb88fd..a8a5869da 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -59,6 +59,7 @@ jobs: upload-reports: runs-on: ubuntu-latest + needs: [variables, wait-for-deploy] env: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD From 5be0b6b96bdff7ae9736f0d6eb63269ec2dfce31 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:03:44 -0700 Subject: [PATCH 043/109] Test manual environment vars --- .github/workflows/daily-csv-upload.yaml | 4 ++-- src/api/views.py | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index a8a5869da..ae73043f1 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,8 +39,8 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; - core.setOutput('environment', environment); + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; + core.setOutput('environment', 'ZA'); wait-for-deploy: runs-on: ubuntu-latest diff --git a/src/api/views.py b/src/api/views.py index c624ee73c..c5deed434 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,5 +1,6 @@ """Internal API views""" import os +from datetime import datetime, timezone from django.apps import apps from django.views.decorators.http import require_http_methods from django.http import FileResponse, HttpResponse, JsonResponse @@ -102,7 +103,11 @@ def get_current_full(request): @require_http_methods(["GET"]) @login_not_required def get_current_federal(request): - # Open the CSV file + now = datetime.now(timezone.utc) + # Check if the current time is 5 AM + if now.hour == 5: + generate_new_file() + file_path = "migrationdata/current-federal.csv" return serve_file(file_path) @@ -114,3 +119,6 @@ def serve_file(file_path): return response else: return HttpResponse("File not found", status=404) + +def generate_new_file(): + pass From 00f66e568d316c4e5fb5eeb67ccbed7584eaae06 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:09:51 -0700 Subject: [PATCH 044/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index ae73043f1..c7b7b60fb 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,8 +39,8 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; - core.setOutput('environment', 'ZA'); + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; + core.setOutput('environment', environment); wait-for-deploy: runs-on: ubuntu-latest @@ -61,8 +61,8 @@ jobs: runs-on: ubuntu-latest needs: [variables, wait-for-deploy] env: - CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME - CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD + CF_USERNAME: CF_ZA_USERNAME + CF_PASSWORD: CF_ZA_PASSWORD steps: - name: Generate current-federal.csv From 1b1a24110137e2192b01118966b06d0217288cf7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:16:45 -0700 Subject: [PATCH 045/109] Remove stubbed code --- src/api/views.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index c5deed434..55c506b3f 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,6 +1,5 @@ """Internal API views""" import os -from datetime import datetime, timezone from django.apps import apps from django.views.decorators.http import require_http_methods from django.http import FileResponse, HttpResponse, JsonResponse @@ -103,11 +102,6 @@ def get_current_full(request): @require_http_methods(["GET"]) @login_not_required def get_current_federal(request): - now = datetime.now(timezone.utc) - # Check if the current time is 5 AM - if now.hour == 5: - generate_new_file() - file_path = "migrationdata/current-federal.csv" return serve_file(file_path) @@ -118,7 +112,4 @@ def serve_file(file_path): response = FileResponse(open(file_path, "rb")) return response else: - return HttpResponse("File not found", status=404) - -def generate_new_file(): - pass + return HttpResponse("File not found", status=404) \ No newline at end of file From dca3eb56765e257918509a9f01bb5fcd48af75de Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:35:35 -0700 Subject: [PATCH 046/109] Add logging / additional validation --- .../commands/generate_current_federal_report.py | 8 ++++++++ .../management/commands/generate_current_full_report.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 83f76f1cc..1a013603e 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -20,6 +20,11 @@ class Command(BaseCommand): # Ensures a slash is added directory = os.path.join(options.get("directory"), "") logger.info("Generating report...") + + # TODO - Delete + current_directory = os.getcwd() + logger.info(f"Current working directory: {current_directory}") + self.generate_current_federal_report(directory) logger.info(f"Success! Created {directory}current-federal.csv") @@ -28,3 +33,6 @@ class Command(BaseCommand): file_path = os.path.join(directory, "current-federal.csv") with open(file_path, "w") as file: csv_export.export_data_federal_to_csv(file) + + if not os.path.exists(file_path): + raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index 92519cdc1..838e2d40d 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -20,6 +20,11 @@ class Command(BaseCommand): # Ensures a slash is added directory = os.path.join(options.get("directory"), "") logger.info("Generating report...") + + # TODO - Delete + current_directory = os.getcwd() + logger.info(f"Current working directory: {current_directory}") + self.generate_current_full_report(directory) logger.info(f"Success! Created {directory}current-full.csv") @@ -28,3 +33,6 @@ class Command(BaseCommand): file_path = os.path.join(directory, "current-full.csv") with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) + + if not os.path.exists(file_path): + raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") From a188137988234a0794fd987c0a8a6f8c06f5c77d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 27 Nov 2023 10:01:05 -0700 Subject: [PATCH 047/109] Test sleep for 20 --- .github/workflows/daily-csv-upload.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index c7b7b60fb..2772e51ac 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -64,6 +64,8 @@ jobs: CF_USERNAME: CF_ZA_USERNAME CF_PASSWORD: CF_ZA_PASSWORD steps: + - name: Wait for a few seconds + run: sleep 20 - name: Generate current-federal.csv uses: 18f/cg-deploy-action@main From f40e71ca8b8fd966b4658b5c7cbbada2b214f6de Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:41:26 -0700 Subject: [PATCH 048/109] Script cleanup / prepare for PR --- .github/workflows/daily-csv-upload.yaml | 49 +++---------------- src/api/views.py | 1 + .../generate_current_federal_report.py | 5 +- .../commands/generate_current_full_report.py | 5 +- 4 files changed, 11 insertions(+), 49 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 2772e51ac..cb2467319 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -7,7 +7,7 @@ on: inputs: environment: type: choice - description: Which environment load reports for? + description: Which environment do you wish to load reports for? options: - stable - staging @@ -24,9 +24,10 @@ on: - bl - rjm - dk - schedule: + # TODO - uncomment after #1403 is finished + #schedule: # Runs every day at 5 AM UTC. - - cron: "0 5 * * *" + # - cron: "0 5 * * *" jobs: variables: @@ -42,31 +43,13 @@ jobs: const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; core.setOutput('environment', environment); - wait-for-deploy: - runs-on: ubuntu-latest - steps: - - name: Wait for deploy to complete - uses: fountainhead/action-wait-for-check@v1.0.0 - id: wait-for-deploy - with: - token: ${{ secrets.GITHUB_TOKEN }} - checkName: "deploy" - ref: ${{ github.event.pull_request.head.sha }} - # the maximum time to wait for the check to complete, in seconds - timeoutSeconds: 600 - # the time to wait between checks, in seconds - intervalSeconds: 10 - upload-reports: runs-on: ubuntu-latest - needs: [variables, wait-for-deploy] + needs: [variables] env: - CF_USERNAME: CF_ZA_USERNAME - CF_PASSWORD: CF_ZA_PASSWORD + CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME + CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: - - name: Wait for a few seconds - run: sleep 20 - - name: Generate current-federal.csv uses: 18f/cg-deploy-action@main with: @@ -84,20 +67,4 @@ jobs: cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} full_command: "cf run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_full_report' --name full" - - comment: - runs-on: ubuntu-latest - needs: [variables, upload-reports] - steps: - - uses: actions/github-script@v6 - env: - ENVIRONMENT: ${{ needs.variables.outputs.environment }} - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: '🥳 Successfully uploaded current-full.csv and current-federal.csv to **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.' - }) + diff --git a/src/api/views.py b/src/api/views.py index 55c506b3f..9bf0cf95f 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -107,6 +107,7 @@ def get_current_federal(request): def serve_file(file_path): """Downloads a file based on a given filepath. Returns a 404 if not found.""" + # TODO - #1403, grab from the S3 instance instead if os.path.exists(file_path): # Serve the CSV file response = FileResponse(open(file_path, "rb")) diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 1a013603e..237f00681 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -21,15 +21,12 @@ class Command(BaseCommand): directory = os.path.join(options.get("directory"), "") logger.info("Generating report...") - # TODO - Delete - current_directory = os.getcwd() - logger.info(f"Current working directory: {current_directory}") - self.generate_current_federal_report(directory) logger.info(f"Success! Created {directory}current-federal.csv") def generate_current_federal_report(self, directory): """Creates a current-full.csv file under the migrationdata/ directory""" + # TODO - #1403, push to the S3 instance instead file_path = os.path.join(directory, "current-federal.csv") with open(file_path, "w") as file: csv_export.export_data_federal_to_csv(file) diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index 838e2d40d..b6915c397 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -21,15 +21,12 @@ class Command(BaseCommand): directory = os.path.join(options.get("directory"), "") logger.info("Generating report...") - # TODO - Delete - current_directory = os.getcwd() - logger.info(f"Current working directory: {current_directory}") - self.generate_current_full_report(directory) logger.info(f"Success! Created {directory}current-full.csv") def generate_current_full_report(self, directory): """Creates a current-full.csv file under the migrationdata/ directory""" + # TODO - #1403, push to the S3 instance instead file_path = os.path.join(directory, "current-full.csv") with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) From fd0afc31b2d06f5d814323d04f3ef654927d2ad3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:59:33 -0700 Subject: [PATCH 049/109] Stubbed test cases --- src/registrar/tests/test_reports.py | 81 +++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 14573ab65..29c48dea5 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -1,3 +1,4 @@ +from unittest import skip from django.test import TestCase from io import StringIO import csv @@ -8,6 +9,86 @@ from django.contrib.auth import get_user_model from registrar.utility.csv_export import export_domains_to_writer +class CsvUploadTest(TestCase): + """Tests to determine if we are uploading our reports correctly""" + def setUp(self): + """Create fake domain data""" + username = "test_user" + first_name = "First" + last_name = "Last" + email = "info@example.com" + self.user = get_user_model().objects.create( + username=username, first_name=first_name, last_name=last_name, email=email + ) + + self.domain_1, _ = Domain.objects.get_or_create(name="cdomain1.gov", state=Domain.State.READY) + self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED) + self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD) + self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN) + self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN) + + self.domain_information_1, _ = DomainInformation.objects.get_or_create( + creator=self.user, + domain=self.domain_1, + organization_type="federal", + federal_agency="World War I Centennial Commission", + federal_type="executive", + ) + self.domain_information_2, _ = DomainInformation.objects.get_or_create( + creator=self.user, + domain=self.domain_2, + organization_type="interstate", + ) + self.domain_information_3, _ = DomainInformation.objects.get_or_create( + creator=self.user, + domain=self.domain_3, + organization_type="federal", + federal_agency="Armed Forces Retirement Home", + ) + self.domain_information_4, _ = DomainInformation.objects.get_or_create( + creator=self.user, + domain=self.domain_4, + organization_type="federal", + federal_agency="Armed Forces Retirement Home", + ) + + def tearDown(self): + """Delete all faked data""" + Domain.objects.all().delete() + DomainInformation.objects.all().delete() + User.objects.all().delete() + super().tearDown() + + @skip("not implemented yet") + def test_generate_federal_report(self): + """Ensures that we correctly generate current-federal.csv""" + raise + + @skip("not implemented yet") + def test_generate_full_report(self): + """Ensures that we correctly generate current-full.csv""" + raise + + @skip("not implemented yet") + def test_api_url_full_report(self): + """Ensures that we correctly return current-full.csv""" + raise + + @skip("not implemented yet") + def test_api_url_federal_report(self): + """Ensures that we correctly return current-full.csv""" + raise + + @skip("not implemented yet") + def test_not_found_full_report(self): + """Ensures that we get a not found when the report doesn't exist""" + raise + + @skip("not implemented yet") + def test_not_found_federal_report(self): + """Ensures that we get a not found when the report doesn't exist""" + raise + class ExportDataTest(TestCase): def setUp(self): username = "test_user" From 7d82ab916e6f76b5f51ab504b9e8cc9c0e60ca77 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:58:58 -0700 Subject: [PATCH 050/109] Test cases --- .../generate_current_federal_report.py | 10 +- .../commands/generate_current_full_report.py | 10 +- src/registrar/tests/test_reports.py | 106 ++++++++++++++---- 3 files changed, 97 insertions(+), 29 deletions(-) diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 237f00681..85ad2f0e4 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -15,21 +15,23 @@ class Command(BaseCommand): def add_arguments(self, parser): """Add our two filename arguments.""" parser.add_argument("--directory", default="migrationdata", help="Desired directory") + parser.add_argument("--checkpath", default=True, help="Used for test cases") def handle(self, **options): # Ensures a slash is added directory = os.path.join(options.get("directory"), "") + check_path = options.get("checkpath") logger.info("Generating report...") - self.generate_current_federal_report(directory) + self.generate_current_federal_report(directory, check_path) logger.info(f"Success! Created {directory}current-federal.csv") - def generate_current_federal_report(self, directory): - """Creates a current-full.csv file under the migrationdata/ directory""" + def generate_current_federal_report(self, directory, check_path): + """Creates a current-full.csv file under the specified directory""" # TODO - #1403, push to the S3 instance instead file_path = os.path.join(directory, "current-federal.csv") with open(file_path, "w") as file: csv_export.export_data_federal_to_csv(file) - if not os.path.exists(file_path): + if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index b6915c397..d1f1545c3 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -15,21 +15,23 @@ class Command(BaseCommand): def add_arguments(self, parser): """Add our two filename arguments.""" parser.add_argument("--directory", default="migrationdata", help="Desired directory") + parser.add_argument("--checkpath", default=True, help="Used for test cases") def handle(self, **options): # Ensures a slash is added directory = os.path.join(options.get("directory"), "") + check_path = options.get("checkpath") logger.info("Generating report...") - self.generate_current_full_report(directory) + self.generate_current_full_report(directory, check_path) logger.info(f"Success! Created {directory}current-full.csv") - def generate_current_full_report(self, directory): - """Creates a current-full.csv file under the migrationdata/ directory""" + def generate_current_full_report(self, directory, check_path): + """Creates a current-full.csv file under the specified directory""" # TODO - #1403, push to the S3 instance instead file_path = os.path.join(directory, "current-full.csv") with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) - if not os.path.exists(file_path): + if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 29c48dea5..482219fb2 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -1,5 +1,5 @@ from unittest import skip -from django.test import TestCase +from django.test import Client, TestCase from io import StringIO import csv from registrar.models.domain_information import DomainInformation @@ -7,12 +7,15 @@ from registrar.models.domain import Domain from registrar.models.user import User from django.contrib.auth import get_user_model from registrar.utility.csv_export import export_domains_to_writer +from django.core.management import call_command +from unittest.mock import call, mock_open, patch - -class CsvUploadTest(TestCase): +class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" def setUp(self): """Create fake domain data""" + self.client = Client(HTTP_HOST="localhost:8080") + username = "test_user" first_name = "First" last_name = "Last" @@ -59,35 +62,96 @@ class CsvUploadTest(TestCase): User.objects.all().delete() super().tearDown() - @skip("not implemented yet") + def test_create_failed_federal(self): + """Ensures that we return an error when we cannot find our created file""" + fake_open = mock_open() + # We don't actually want to write anything for a test case, + # we just want to verify what is being written. + with patch('builtins.open', fake_open), self.assertRaises(FileNotFoundError) as err: + call_command( + "generate_current_federal_report" + ) + error = err.exception + self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-federal.csv'") + + def test_create_failed_full(self): + """Ensures that we return an error when we cannot find our created file""" + fake_open = mock_open() + # We don't actually want to write anything for a test case, + # we just want to verify what is being written. + with patch('builtins.open', fake_open), self.assertRaises(FileNotFoundError) as err: + call_command( + "generate_current_full_report" + ) + error = err.exception + self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-full.csv'") + def test_generate_federal_report(self): """Ensures that we correctly generate current-federal.csv""" - raise + expected_file_content = [ + call('Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n'), + call('cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n'), + call('ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n') + ] + fake_open = mock_open() + # We don't actually want to write anything for a test case, + # we just want to verify what is being written. + with patch('builtins.open', fake_open): + call_command( + "generate_current_federal_report", + checkpath=False + ) + content = fake_open() + # Now you can make assertions about how you expect 'file' to be used. + content.write.assert_has_calls(expected_file_content) - @skip("not implemented yet") def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" - raise - - @skip("not implemented yet") - def test_api_url_full_report(self): - """Ensures that we correctly return current-full.csv""" - raise - - @skip("not implemented yet") - def test_api_url_federal_report(self): - """Ensures that we correctly return current-full.csv""" - raise + expected_file_content = [ + call('Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n'), + call('cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n'), + call('ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n'), + call('adomain2.gov,Interstate,,,,, \r\n') + ] + fake_open = mock_open() + # We don't actually want to write anything for a test case, + # we just want to verify what is being written. + with patch('builtins.open', fake_open): + call_command( + "generate_current_full_report", + checkpath=False + ) + content = fake_open() + # Now you can make assertions about how you expect 'file' to be used. + content.write.assert_has_calls(expected_file_content) - @skip("not implemented yet") def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" - raise + response = self.client.get('/api/v1/get-report/current-full') + + # Check that the response has status code 404 + self.assertEqual(response.status_code, 404) + # Check that the response body contains "File not found" + self.assertEqual(response.content.decode(), "File not found") - @skip("not implemented yet") def test_not_found_federal_report(self): """Ensures that we get a not found when the report doesn't exist""" - raise + response = self.client.get('/api/v1/get-report/current-federal') + + # Check that the response has status code 404 + self.assertEqual(response.status_code, 404) + # Check that the response body contains "File not found" + self.assertEqual(response.content.decode(), "File not found") + + def test_federal_report(self): + """Ensures that we get a not found when the report doesn't exist""" + response = self.client.get('/api/v1/get-report/current-federal') + + # Check that the response has status code 404 + self.assertEqual(response.status_code, 404) + # Check that the response body contains "File not found" + self.assertEqual(response.content.decode(), "File not found") + class ExportDataTest(TestCase): def setUp(self): From 63dd354da89dfda79f11317e0801ad38f43cdbdd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:03:25 -0700 Subject: [PATCH 051/109] Tests --- src/api/views.py | 5 +- .../tests/data/fake_current_federal.csv | 3 + src/registrar/tests/test_reports.py | 82 ++++++++++--------- 3 files changed, 47 insertions(+), 43 deletions(-) create mode 100644 src/registrar/tests/data/fake_current_federal.csv diff --git a/src/api/views.py b/src/api/views.py index 9bf0cf95f..06801897d 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -93,7 +93,7 @@ def available(request, domain=""): @require_http_methods(["GET"]) @login_not_required -def get_current_full(request): +def get_current_full(request, file_path = "migrationdata/current-full.csv"): # Open the CSV file file_path = "migrationdata/current-full.csv" return serve_file(file_path) @@ -101,8 +101,7 @@ def get_current_full(request): @require_http_methods(["GET"]) @login_not_required -def get_current_federal(request): - file_path = "migrationdata/current-federal.csv" +def get_current_federal(request, file_path = "migrationdata/current-federal.csv"): return serve_file(file_path) def serve_file(file_path): diff --git a/src/registrar/tests/data/fake_current_federal.csv b/src/registrar/tests/data/fake_current_federal.csv new file mode 100644 index 000000000..33f679e9e --- /dev/null +++ b/src/registrar/tests/data/fake_current_federal.csv @@ -0,0 +1,3 @@ +Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email +cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, +ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \ No newline at end of file diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 482219fb2..8a61051ec 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -1,21 +1,25 @@ -from unittest import skip -from django.test import Client, TestCase -from io import StringIO import csv +from unittest import skip +from django.test import Client, RequestFactory, TestCase +from io import StringIO from registrar.models.domain_information import DomainInformation from registrar.models.domain import Domain from registrar.models.user import User +from os import linesep from django.contrib.auth import get_user_model from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command from unittest.mock import call, mock_open, patch +from api.views import get_current_federal, get_current_full + class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" + def setUp(self): """Create fake domain data""" self.client = Client(HTTP_HOST="localhost:8080") - + self.factory = RequestFactory() username = "test_user" first_name = "First" last_name = "Last" @@ -67,10 +71,8 @@ class CsvReportsTest(TestCase): fake_open = mock_open() # We don't actually want to write anything for a test case, # we just want to verify what is being written. - with patch('builtins.open', fake_open), self.assertRaises(FileNotFoundError) as err: - call_command( - "generate_current_federal_report" - ) + with patch("builtins.open", fake_open), self.assertRaises(FileNotFoundError) as err: + call_command("generate_current_federal_report") error = err.exception self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-federal.csv'") @@ -79,28 +81,23 @@ class CsvReportsTest(TestCase): fake_open = mock_open() # We don't actually want to write anything for a test case, # we just want to verify what is being written. - with patch('builtins.open', fake_open), self.assertRaises(FileNotFoundError) as err: - call_command( - "generate_current_full_report" - ) + with patch("builtins.open", fake_open), self.assertRaises(FileNotFoundError) as err: + call_command("generate_current_full_report") error = err.exception self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-full.csv'") def test_generate_federal_report(self): """Ensures that we correctly generate current-federal.csv""" expected_file_content = [ - call('Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n'), - call('cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n'), - call('ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n') + call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), + call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), + call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, # we just want to verify what is being written. - with patch('builtins.open', fake_open): - call_command( - "generate_current_federal_report", - checkpath=False - ) + with patch("builtins.open", fake_open): + call_command("generate_current_federal_report", checkpath=False) content = fake_open() # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) @@ -108,26 +105,23 @@ class CsvReportsTest(TestCase): def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" expected_file_content = [ - call('Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n'), - call('cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n'), - call('ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n'), - call('adomain2.gov,Interstate,,,,, \r\n') + call(f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email{linesep}"), + call(f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, {linesep}"), + call(f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,, {linesep}"), + call(f"adomain2.gov,Interstate,,,,, {linesep}"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, # we just want to verify what is being written. - with patch('builtins.open', fake_open): - call_command( - "generate_current_full_report", - checkpath=False - ) + with patch("builtins.open", fake_open): + call_command("generate_current_full_report", checkpath=False) content = fake_open() # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) - + def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" - response = self.client.get('/api/v1/get-report/current-full') + response = self.client.get("/api/v1/get-report/current-full") # Check that the response has status code 404 self.assertEqual(response.status_code, 404) @@ -136,21 +130,29 @@ class CsvReportsTest(TestCase): def test_not_found_federal_report(self): """Ensures that we get a not found when the report doesn't exist""" - response = self.client.get('/api/v1/get-report/current-federal') + response = self.client.get("/api/v1/get-report/current-federal") # Check that the response has status code 404 self.assertEqual(response.status_code, 404) # Check that the response body contains "File not found" self.assertEqual(response.content.decode(), "File not found") - - def test_federal_report(self): - """Ensures that we get a not found when the report doesn't exist""" - response = self.client.get('/api/v1/get-report/current-federal') - # Check that the response has status code 404 - self.assertEqual(response.status_code, 404) - # Check that the response body contains "File not found" - self.assertEqual(response.content.decode(), "File not found") + def test_load_federal_report(self): + """Tests the current-federal api link""" + self.maxDiff = None + request = self.factory.get("/fake-path") + response = get_current_federal(request, file_path="registrar/tests/data/fake_current_federal.csv") + # Check that the response has status code 200 + self.assertEqual(response.status_code, 200) + # Check that the response contains what we expect + file_content = b"".join(response.streaming_content).decode("utf-8") + expected_file_content = ( + f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email{linesep}" + f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, {linesep}" + f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,, {linesep}" + ) + + self.assertEqual(file_content, expected_file_content) class ExportDataTest(TestCase): From 4594eb7ca3c59bd524c026d15050fb2d4bbb313c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:15:58 -0700 Subject: [PATCH 052/109] Final test cases --- .github/workflows/daily-csv-upload.yaml | 1 - .../tests/data/fake_current_full.csv | 4 +++ src/registrar/tests/test_reports.py | 33 ++++++++++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 src/registrar/tests/data/fake_current_full.csv diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index cb2467319..174244ad6 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,7 +2,6 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: - pull_request: workflow_dispatch: inputs: environment: diff --git a/src/registrar/tests/data/fake_current_full.csv b/src/registrar/tests/data/fake_current_full.csv new file mode 100644 index 000000000..43eefc271 --- /dev/null +++ b/src/registrar/tests/data/fake_current_full.csv @@ -0,0 +1,4 @@ +Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email +cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, +ddomain3.gov,Federal,Armed Forces Retirement Home,,,, +adomain2.gov,Interstate,,,,, \ No newline at end of file diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 8a61051ec..3fff8a8a7 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -5,7 +5,6 @@ from io import StringIO from registrar.models.domain_information import DomainInformation from registrar.models.domain import Domain from registrar.models.user import User -from os import linesep from django.contrib.auth import get_user_model from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command @@ -105,10 +104,10 @@ class CsvReportsTest(TestCase): def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" expected_file_content = [ - call(f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email{linesep}"), - call(f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, {linesep}"), - call(f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,, {linesep}"), - call(f"adomain2.gov,Interstate,,,,, {linesep}"), + call(f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), + call(f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), + call(f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), + call(f"adomain2.gov,Interstate,,,,, \r\n"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, @@ -147,9 +146,27 @@ class CsvReportsTest(TestCase): # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( - f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email{linesep}" - f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, {linesep}" - f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,, {linesep}" + f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" + f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" + f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,," + ) + + self.assertEqual(file_content, expected_file_content) + + def test_load_full_report(self): + """Tests the current-federal api link""" + self.maxDiff = None + request = self.factory.get("/fake-path") + response = get_current_federal(request, file_path="registrar/tests/data/fake_current_full.csv") + # Check that the response has status code 200 + self.assertEqual(response.status_code, 200) + # Check that the response contains what we expect + file_content = b"".join(response.streaming_content).decode("utf-8") + expected_file_content = ( + f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" + f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" + f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\r\n" + f"adomain2.gov,Interstate,,,,," ) self.assertEqual(file_content, expected_file_content) From 14355327b29a066962fdc6234808a5596f74dd4a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:24:44 -0700 Subject: [PATCH 053/109] Linting and comments --- src/api/views.py | 7 ++++--- .../commands/generate_current_federal_report.py | 7 ++++++- .../management/commands/generate_current_full_report.py | 9 +++++++-- src/registrar/tests/test_reports.py | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 06801897d..b7c84a521 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -93,7 +93,7 @@ def available(request, domain=""): @require_http_methods(["GET"]) @login_not_required -def get_current_full(request, file_path = "migrationdata/current-full.csv"): +def get_current_full(request, file_path="migrationdata/current-full.csv"): # Open the CSV file file_path = "migrationdata/current-full.csv" return serve_file(file_path) @@ -101,9 +101,10 @@ def get_current_full(request, file_path = "migrationdata/current-full.csv"): @require_http_methods(["GET"]) @login_not_required -def get_current_federal(request, file_path = "migrationdata/current-federal.csv"): +def get_current_federal(request, file_path="migrationdata/current-federal.csv"): return serve_file(file_path) + def serve_file(file_path): """Downloads a file based on a given filepath. Returns a 404 if not found.""" # TODO - #1403, grab from the S3 instance instead @@ -112,4 +113,4 @@ def serve_file(file_path): response = FileResponse(open(file_path, "rb")) return response else: - return HttpResponse("File not found", status=404) \ No newline at end of file + return HttpResponse("File not found", status=404) diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 85ad2f0e4..8abb4f493 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -15,9 +15,14 @@ class Command(BaseCommand): def add_arguments(self, parser): """Add our two filename arguments.""" parser.add_argument("--directory", default="migrationdata", help="Desired directory") - parser.add_argument("--checkpath", default=True, help="Used for test cases") + parser.add_argument( + "--checkpath", + default=True, + help="Flag that determines if we do a check for os.path.exists. Used for test cases", + ) def handle(self, **options): + """Grabs the directory then creates current-federal.csv in that directory""" # Ensures a slash is added directory = os.path.join(options.get("directory"), "") check_path = options.get("checkpath") diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index d1f1545c3..7db334186 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -15,9 +15,14 @@ class Command(BaseCommand): def add_arguments(self, parser): """Add our two filename arguments.""" parser.add_argument("--directory", default="migrationdata", help="Desired directory") - parser.add_argument("--checkpath", default=True, help="Used for test cases") + parser.add_argument( + "--checkpath", + default=True, + help="Flag that determines if we do a check for os.path.exists. Used for test cases", + ) def handle(self, **options): + """Grabs the directory then creates current-full.csv in that directory""" # Ensures a slash is added directory = os.path.join(options.get("directory"), "") check_path = options.get("checkpath") @@ -32,6 +37,6 @@ class Command(BaseCommand): file_path = os.path.join(directory, "current-full.csv") with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) - + if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 3fff8a8a7..898437d06 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -152,7 +152,7 @@ class CsvReportsTest(TestCase): ) self.assertEqual(file_content, expected_file_content) - + def test_load_full_report(self): """Tests the current-federal api link""" self.maxDiff = None From 2faad04e9e14a2b7f9b274a01cc645c8a3428108 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:43:06 -0700 Subject: [PATCH 054/109] Linting changes --- src/api/views.py | 2 -- src/registrar/tests/test_reports.py | 31 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index b7c84a521..15515dd3a 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -94,8 +94,6 @@ def available(request, domain=""): @require_http_methods(["GET"]) @login_not_required def get_current_full(request, file_path="migrationdata/current-full.csv"): - # Open the CSV file - file_path = "migrationdata/current-full.csv" return serve_file(file_path) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 898437d06..2674823ee 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -1,5 +1,4 @@ import csv -from unittest import skip from django.test import Client, RequestFactory, TestCase from io import StringIO from registrar.models.domain_information import DomainInformation @@ -88,9 +87,9 @@ class CsvReportsTest(TestCase): def test_generate_federal_report(self): """Ensures that we correctly generate current-federal.csv""" expected_file_content = [ - call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), - call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), - call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), + call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n"), + call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \n"), + call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \n"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, @@ -104,10 +103,10 @@ class CsvReportsTest(TestCase): def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" expected_file_content = [ - call(f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), - call(f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), - call(f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), - call(f"adomain2.gov,Interstate,,,,, \r\n"), + call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n"), + call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \n"), + call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \n"), + call("adomain2.gov,Interstate,,,,, \n"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, @@ -146,9 +145,9 @@ class CsvReportsTest(TestCase): # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( - f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" - f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" - f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,," + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" + "ddomain3.gov,Federal,Armed Forces Retirement Home,,,," ) self.assertEqual(file_content, expected_file_content) @@ -157,16 +156,16 @@ class CsvReportsTest(TestCase): """Tests the current-federal api link""" self.maxDiff = None request = self.factory.get("/fake-path") - response = get_current_federal(request, file_path="registrar/tests/data/fake_current_full.csv") + response = get_current_full(request, file_path="registrar/tests/data/fake_current_full.csv") # Check that the response has status code 200 self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( - f"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" - f"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" - f"ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\r\n" - f"adomain2.gov,Interstate,,,,," + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" + "ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\n" + "adomain2.gov,Interstate,,,,," ) self.assertEqual(file_content, expected_file_content) From cf68aaea5b5f021799f8d69c3368fc6266f46073 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:52:34 -0700 Subject: [PATCH 055/109] Update test_reports.py --- src/registrar/tests/test_reports.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 2674823ee..160f80ef9 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -87,9 +87,9 @@ class CsvReportsTest(TestCase): def test_generate_federal_report(self): """Ensures that we correctly generate current-federal.csv""" expected_file_content = [ - call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n"), - call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \n"), - call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \n"), + call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), + call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), + call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, @@ -103,10 +103,10 @@ class CsvReportsTest(TestCase): def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" expected_file_content = [ - call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n"), - call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \n"), - call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \n"), - call("adomain2.gov,Interstate,,,,, \n"), + call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), + call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), + call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), + call("adomain2.gov,Interstate,,,,, \r\n"), ] fake_open = mock_open() # We don't actually want to write anything for a test case, @@ -145,8 +145,8 @@ class CsvReportsTest(TestCase): # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( - "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,,,," ) @@ -162,9 +162,9 @@ class CsvReportsTest(TestCase): # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( - "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" - "ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\n" + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" + "ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\r\n" "adomain2.gov,Interstate,,,,," ) From 328c0c0816866f418835c93c9f5ed94c986e471c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:08:57 -0700 Subject: [PATCH 056/109] Update test_reports.py --- src/registrar/tests/test_reports.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 160f80ef9..9f118a021 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -145,8 +145,8 @@ class CsvReportsTest(TestCase): # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( - "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email \r\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,,,," ) @@ -163,8 +163,8 @@ class CsvReportsTest(TestCase): file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n" - "ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\r\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" + "ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n" "adomain2.gov,Interstate,,,,," ) From 00976b3b9b9022a46cce8e0a23d61aa350e42257 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:24:45 -0700 Subject: [PATCH 057/109] Add temp logging to find error --- src/registrar/tests/test_reports.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 9f118a021..f79f4be56 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -144,6 +144,8 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") + print("data to expect fed") + print(file_content) expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email \r\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" @@ -161,6 +163,8 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") + print("data to expect") + print(file_content) expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" From 37456d57cc39cca5ea12dca5b3e264dadb861985 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:30:08 -0700 Subject: [PATCH 058/109] Update test_reports.py --- src/registrar/tests/test_reports.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index f79f4be56..b631f6040 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -9,7 +9,9 @@ from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command from unittest.mock import call, mock_open, patch from api.views import get_current_federal, get_current_full +import logging +logger = logging.getLogger(__name__) class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" @@ -144,8 +146,7 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") - print("data to expect fed") - print(file_content) + logger.info(f"data to expect fed: {file_content}") expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email \r\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" @@ -163,8 +164,7 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") - print("data to expect") - print(file_content) + logger.info(f"data to expect full: {file_content}") expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" From e23ef4f5eb2c5504bca14a21c06a44e28fda8e41 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:35:25 -0700 Subject: [PATCH 059/109] Update test_reports.py --- src/registrar/tests/test_reports.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index b631f6040..ff734193d 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -9,9 +9,7 @@ from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command from unittest.mock import call, mock_open, patch from api.views import get_current_federal, get_current_full -import logging -logger = logging.getLogger(__name__) class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" @@ -146,10 +144,10 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") - logger.info(f"data to expect fed: {file_content}") + expected_file_content = ( - "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email \r\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \n" "ddomain3.gov,Federal,Armed Forces Retirement Home,,,," ) @@ -164,11 +162,10 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 200) # Check that the response contains what we expect file_content = b"".join(response.streaming_content).decode("utf-8") - logger.info(f"data to expect full: {file_content}") expected_file_content = ( - "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n" - "ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n" + "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" + "ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\n" "adomain2.gov,Interstate,,,,," ) From 3f1a8b4ef597cb3006ec6e09ec8573694ba075b6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:38:58 -0700 Subject: [PATCH 060/109] Fix test cases --- src/registrar/tests/test_reports.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index ff734193d..cb1d6a0b9 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -137,7 +137,6 @@ class CsvReportsTest(TestCase): def test_load_federal_report(self): """Tests the current-federal api link""" - self.maxDiff = None request = self.factory.get("/fake-path") response = get_current_federal(request, file_path="registrar/tests/data/fake_current_federal.csv") # Check that the response has status code 200 @@ -147,7 +146,7 @@ class CsvReportsTest(TestCase): expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" - "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \n" + "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,,,," ) @@ -155,7 +154,6 @@ class CsvReportsTest(TestCase): def test_load_full_report(self): """Tests the current-federal api link""" - self.maxDiff = None request = self.factory.get("/fake-path") response = get_current_full(request, file_path="registrar/tests/data/fake_current_full.csv") # Check that the response has status code 200 From f5974e00dcb103afb20fea7160bd8d5954454332 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:55:45 -0700 Subject: [PATCH 061/109] Create s3_bucket.py --- src/registrar/utility/s3_bucket.py | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/registrar/utility/s3_bucket.py diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py new file mode 100644 index 000000000..af59fdb67 --- /dev/null +++ b/src/registrar/utility/s3_bucket.py @@ -0,0 +1,62 @@ +"""Utilities for accessing an AWS S3 bucket""" + +import boto3 + +from django.conf import settings +from django.template.loader import get_template + + +class S3ClientError(RuntimeError): + """Local error for handling all failures with boto3.client""" + pass + + +class S3ClientHelper: + """Helper class that simplifies S3 intialization""" + def __init__(self): + try: + self.boto_client = boto3.client( + "s3", + region_name=settings.AWS_S3_REGION, + aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID, + aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY + #config=settings.BOTO_CONFIG, + ) + except Exception as exc: + raise S3ClientError("Could not access the S3 client.") from exc + + #self.bucket_name = + print("here:") + bucket = self.list_objects() + print(bucket) + + def get_bucket_name(self): + """Gets the name of our S3 Bucket""" + return settings.AWS_S3_BUCKET_NAME + + def list_objects(self): + """Returns a list of the top 1000 objects within our S3 instance""" + try: + response = self.boto_client.list_objects_v2(Bucket=self.get_bucket_name()) + except Exception as exc: + raise S3ClientError("Couldn't list objects") from exc + return response + + def upload_file(self, file_path, file_name): + """Uploads a file to our S3 instance""" + try: + response = self.boto_client.upload_file(file_path, self.get_bucket_name(), file_name) + except Exception as exc: + raise S3ClientError("Couldn't upload file") from exc + return response + + def get_file(self, file_name, decode_to_utf=False): + """Gets a file to our S3 instance and returns the file content""" + try: + response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name) + except Exception as exc: + raise S3ClientError("Couldn't get file") from exc + file_content = response["Body"].read() + if decode_to_utf: + return file_content.decode("utf-8") + return file_content From e78a506d2b2cde785b357c65888bf91595236f4c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 08:36:12 -0700 Subject: [PATCH 062/109] Link bucket to generator --- src/api/views.py | 13 +++++++++---- .../commands/generate_current_full_report.py | 16 ++++++++++++---- src/registrar/utility/s3_bucket.py | 13 +++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 15515dd3a..ddd96f744 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -9,6 +9,8 @@ from login_required import login_not_required from cachetools.func import ttl_cache +from registrar.utility.s3_bucket import S3ClientHelper + DOMAIN_FILE_URL = "https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv" @@ -94,21 +96,24 @@ def available(request, domain=""): @require_http_methods(["GET"]) @login_not_required def get_current_full(request, file_path="migrationdata/current-full.csv"): - return serve_file(file_path) + return serve_file(file_path, "current-full.csv") @require_http_methods(["GET"]) @login_not_required def get_current_federal(request, file_path="migrationdata/current-federal.csv"): - return serve_file(file_path) + return serve_file(file_path, "current-federal.csv") -def serve_file(file_path): +def serve_file(file_path, file_name): """Downloads a file based on a given filepath. Returns a 404 if not found.""" + s3_client = S3ClientHelper() # TODO - #1403, grab from the S3 instance instead + # TODO - check if file exists in s3, not here if os.path.exists(file_path): # Serve the CSV file - response = FileResponse(open(file_path, "rb")) + file = s3_client.get_file(file_name) + response = FileResponse(file) return response else: return HttpResponse("File not found", status=404) diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index 7db334186..f9ed12ba5 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -4,6 +4,7 @@ import os from django.core.management import BaseCommand from registrar.utility import csv_export +from registrar.utility.s3_bucket import S3ClientHelper logger = logging.getLogger(__name__) @@ -23,20 +24,27 @@ class Command(BaseCommand): def handle(self, **options): """Grabs the directory then creates current-full.csv in that directory""" + file_name = "current-full.csv" # Ensures a slash is added directory = os.path.join(options.get("directory"), "") check_path = options.get("checkpath") logger.info("Generating report...") - self.generate_current_full_report(directory, check_path) - logger.info(f"Success! Created {directory}current-full.csv") + self.generate_current_full_report(directory, file_name, check_path) - def generate_current_full_report(self, directory, check_path): + file_path = os.path.join(directory, file_name) + logger.info(f"Success! Created {file_path}") + + def generate_current_full_report(self, directory, file_name, check_path): """Creates a current-full.csv file under the specified directory""" + s3_client = S3ClientHelper() # TODO - #1403, push to the S3 instance instead - file_path = os.path.join(directory, "current-full.csv") + file_path = os.path.join(directory, file_name) + # TODO - Don't genererate a useless file with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") + + s3_client.upload_file(file_path, file_name) diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py index af59fdb67..959c6b23a 100644 --- a/src/registrar/utility/s3_bucket.py +++ b/src/registrar/utility/s3_bucket.py @@ -25,15 +25,10 @@ class S3ClientHelper: except Exception as exc: raise S3ClientError("Could not access the S3 client.") from exc - #self.bucket_name = - print("here:") - bucket = self.list_objects() - print(bucket) - def get_bucket_name(self): """Gets the name of our S3 Bucket""" return settings.AWS_S3_BUCKET_NAME - + def list_objects(self): """Returns a list of the top 1000 objects within our S3 instance""" try: @@ -49,14 +44,16 @@ class S3ClientHelper: except Exception as exc: raise S3ClientError("Couldn't upload file") from exc return response - + def get_file(self, file_name, decode_to_utf=False): """Gets a file to our S3 instance and returns the file content""" try: response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name) except Exception as exc: raise S3ClientError("Couldn't get file") from exc + file_content = response["Body"].read() if decode_to_utf: return file_content.decode("utf-8") - return file_content + else: + return file_content From 5530db82fdcaca3ec42aca92fb7a23ebbbe77725 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 08:42:09 -0700 Subject: [PATCH 063/109] Add comments --- src/api/views.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/api/views.py b/src/api/views.py index 782a770ef..7dc336460 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -102,12 +102,18 @@ def available(request, domain=""): @require_http_methods(["GET"]) @login_not_required def get_current_full(request, file_path="migrationdata/current-full.csv"): + """This will return the file content of current-full.csv which is the command + output of generate_current_full_report.py. This command iterates through each Domain + and returns a CSV representation.""" return serve_file(file_path) @require_http_methods(["GET"]) @login_not_required def get_current_federal(request, file_path="migrationdata/current-federal.csv"): + """This will return the file content of current-federal.csv which is the command + output of generate_current_federal_report.py. This command iterates through each Domain + and returns a CSV representation.""" return serve_file(file_path) From 59950a1fde709293f32c05d6b07818426822615e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 11:57:17 -0700 Subject: [PATCH 064/109] Change logging and add secrets --- .github/workflows/daily-csv-upload.yaml | 18 +++++++++- src/api/views.py | 33 ++++++++++--------- src/registrar/config/settings.py | 12 +++++++ .../generate_current_federal_report.py | 27 +++++++++++---- .../commands/generate_current_full_report.py | 24 +++++++++----- src/registrar/utility/s3_bucket.py | 9 ++--- 6 files changed, 86 insertions(+), 37 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 174244ad6..5925cffcb 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,6 +2,7 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: + pull_request: workflow_dispatch: inputs: environment: @@ -42,9 +43,24 @@ jobs: const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; core.setOutput('environment', environment); + wait-for-deploy: + runs-on: ubuntu-latest + steps: + - name: Wait for deploy to complete + uses: fountainhead/action-wait-for-check@v1.0.0 + id: wait-for-deploy + with: + token: ${{ secrets.GITHUB_TOKEN }} + checkName: "deploy" + ref: ${{ github.event.pull_request.head.sha }} + # the maximum time to wait for the check to complete, in seconds + timeoutSeconds: 600 + # the time to wait between checks, in seconds + intervalSeconds: 10 + upload-reports: runs-on: ubuntu-latest - needs: [variables] + needs: [variables, wait-for-deploy] env: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD diff --git a/src/api/views.py b/src/api/views.py index 9f077fea6..60f11023e 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -12,7 +12,7 @@ from login_required import login_not_required from cachetools.func import ttl_cache -from registrar.utility.s3_bucket import S3ClientHelper +from registrar.utility.s3_bucket import S3ClientError, S3ClientHelper DOMAIN_FILE_URL = "https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv" @@ -103,31 +103,32 @@ def available(request, domain=""): @require_http_methods(["GET"]) @login_not_required -def get_current_full(request, file_path="migrationdata/current-full.csv"): +def get_current_full(request, file_name="current-full.csv"): """This will return the file content of current-full.csv which is the command output of generate_current_full_report.py. This command iterates through each Domain and returns a CSV representation.""" - return serve_file(file_path, "current-full.csv") + return serve_file(file_name) @require_http_methods(["GET"]) @login_not_required -def get_current_federal(request, file_path="migrationdata/current-federal.csv"): +def get_current_federal(request, file_name="current-federal.csv"): """This will return the file content of current-federal.csv which is the command output of generate_current_federal_report.py. This command iterates through each Domain and returns a CSV representation.""" - return serve_file(file_path, "current-federal.csv") + return serve_file(file_name) -def serve_file(file_path, file_name): - """Downloads a file based on a given filepath. Returns a 404 if not found.""" +def serve_file(file_name): + """Downloads a file based on a given filepath. Returns a 500 if not found.""" s3_client = S3ClientHelper() - # TODO - #1403, grab from the S3 instance instead - # TODO - check if file exists in s3, not here - if os.path.exists(file_path): - # Serve the CSV file - file = s3_client.get_file(file_name) - response = FileResponse(file) - return response - else: - return HttpResponse("File not found", status=404) + # Serve the CSV file. If not found, an exception will be thrown. + # This will then be caught by flat, causing it to not read it - which is what we want. + try: + file = s3_client.get_file(file_name, decode_to_utf=True) + except S3ClientError as err: + # TODO - #1317: Notify operations when auto report generation fails + raise err + + response = HttpResponse(file) + return response diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 3487b1b66..5de143cb1 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -58,6 +58,11 @@ secret_key = secret("DJANGO_SECRET_KEY") secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None) secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None) +aws_s3_region_name = secret("AWS_S3_ACCESS_KEY_ID", None) +secret_aws_s3_key_id = secret("AWS_S3_SECRET_ACCESS_KEY", None) +secret_aws_s3_key = secret("AWS_S3_REGION", None) +secret_aws_s3_bucket_name = secret("AWS_S3_BUCKET_NAME", None) + secret_registry_cl_id = secret("REGISTRY_CL_ID") secret_registry_password = secret("REGISTRY_PASSWORD") secret_registry_cert = b64decode(secret("REGISTRY_CERT", "")) @@ -257,6 +262,13 @@ AUTH_USER_MODEL = "registrar.User" AWS_ACCESS_KEY_ID = secret_aws_ses_key_id AWS_SECRET_ACCESS_KEY = secret_aws_ses_key AWS_REGION = "us-gov-west-1" + +# Configuration for accessing AWS S3 +AWS_S3_ACCESS_KEY_ID = secret_aws_s3_key_id +AWS_S3_SECRET_ACCESS_KEY = secret_aws_s3_key +AWS_S3_REGION = aws_s3_region_name +AWS_S3_BUCKET_NAME = secret_aws_s3_bucket_name + # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html#standard-retry-mode AWS_RETRY_MODE: Final = "standard" # base 2 exponential backoff with max of 20 seconds: diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 8abb4f493..6075d8ebb 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -4,6 +4,7 @@ import os from django.core.management import BaseCommand from registrar.utility import csv_export +from registrar.utility.s3_bucket import S3ClientHelper logger = logging.getLogger(__name__) @@ -23,20 +24,32 @@ class Command(BaseCommand): def handle(self, **options): """Grabs the directory then creates current-federal.csv in that directory""" + file_name = "current-federal.csv" # Ensures a slash is added directory = os.path.join(options.get("directory"), "") check_path = options.get("checkpath") + logger.info("Generating report...") + try: + self.generate_current_federal_report(directory, file_name, check_path) + except Exception as err: + # TODO - #1317: Notify operations when auto report generation fails + raise err + else: + logger.info(f"Success! Created {file_name}") - self.generate_current_federal_report(directory, check_path) - logger.info(f"Success! Created {directory}current-federal.csv") - - def generate_current_federal_report(self, directory, check_path): - """Creates a current-full.csv file under the specified directory""" - # TODO - #1403, push to the S3 instance instead - file_path = os.path.join(directory, "current-federal.csv") + def generate_current_federal_report(self, directory, file_name, check_path): + """Creates a current-full.csv file under the specified directory, + then uploads it to a AWS S3 bucket""" + s3_client = S3ClientHelper() + file_path = os.path.join(directory, file_name) + + # Generate a file locally for upload with open(file_path, "w") as file: csv_export.export_data_federal_to_csv(file) if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") + + # Upload this generated file for our S3 instance + s3_client.upload_file(file_path, file_name) diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index f9ed12ba5..83be4f9b5 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -28,23 +28,29 @@ class Command(BaseCommand): # Ensures a slash is added directory = os.path.join(options.get("directory"), "") check_path = options.get("checkpath") + logger.info("Generating report...") - - self.generate_current_full_report(directory, file_name, check_path) - - file_path = os.path.join(directory, file_name) - logger.info(f"Success! Created {file_path}") + try: + self.generate_current_full_report(directory, file_name, check_path) + except Exception as err: + # TODO - #1317: Notify operations when auto report generation fails + raise err + else: + logger.info(f"Success! Created {file_name}") def generate_current_full_report(self, directory, file_name, check_path): - """Creates a current-full.csv file under the specified directory""" + """Creates a current-full.csv file under the specified directory, + then uploads it to a AWS S3 bucket""" s3_client = S3ClientHelper() - # TODO - #1403, push to the S3 instance instead file_path = os.path.join(directory, file_name) - # TODO - Don't genererate a useless file + + # Generate a file locally for upload with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") - + + # Upload this generated file for our S3 instance s3_client.upload_file(file_path, file_name) + diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py index 959c6b23a..9e7f0da31 100644 --- a/src/registrar/utility/s3_bucket.py +++ b/src/registrar/utility/s3_bucket.py @@ -19,8 +19,8 @@ class S3ClientHelper: "s3", region_name=settings.AWS_S3_REGION, aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID, - aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY - #config=settings.BOTO_CONFIG, + aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY, + config=settings.BOTO_CONFIG, ) except Exception as exc: raise S3ClientError("Could not access the S3 client.") from exc @@ -49,9 +49,10 @@ class S3ClientHelper: """Gets a file to our S3 instance and returns the file content""" try: response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name) + except self.boto_client.exceptions.NoSuchKey as exc: + raise S3ClientError("File was not found") from exc except Exception as exc: - raise S3ClientError("Couldn't get file") from exc - + raise S3ClientError("Couldn't get file, an unspecified error occured") from exc file_content = response["Body"].read() if decode_to_utf: return file_content.decode("utf-8") From e90d254339131ebd115370b4992798e1768e0da2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:50:59 -0700 Subject: [PATCH 065/109] Test s3 config settings --- src/registrar/config/settings.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 5de143cb1..a76f87847 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -38,6 +38,13 @@ if key_service and key_service.credentials: else: secret = env +# Get secrets from Cloud.gov user provided s3 service, if exists +# If not, get secrets from environment variables. +s3_key_service = AppEnv().get_service(name="getgov-s3") +if s3_key_service and s3_key_service.credentials: + secret_s3 = s3_key_service.credentials.get +else: + secret_s3 = None # # # ### # Values obtained externally # # # # ### @@ -58,10 +65,10 @@ secret_key = secret("DJANGO_SECRET_KEY") secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None) secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None) -aws_s3_region_name = secret("AWS_S3_ACCESS_KEY_ID", None) -secret_aws_s3_key_id = secret("AWS_S3_SECRET_ACCESS_KEY", None) -secret_aws_s3_key = secret("AWS_S3_REGION", None) -secret_aws_s3_bucket_name = secret("AWS_S3_BUCKET_NAME", None) +aws_s3_region_name = secret_s3("region", None) +secret_aws_s3_key_id = secret_s3("access_key_id", None) +secret_aws_s3_key = secret_s3("secret_access_key", None) +secret_aws_s3_bucket_name = secret_s3("bucket", None) secret_registry_cl_id = secret("REGISTRY_CL_ID") secret_registry_password = secret("REGISTRY_PASSWORD") @@ -269,7 +276,7 @@ AWS_S3_SECRET_ACCESS_KEY = secret_aws_s3_key AWS_S3_REGION = aws_s3_region_name AWS_S3_BUCKET_NAME = secret_aws_s3_bucket_name -# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html#standard-retry-mode +# https://boto3.amazonaws.com/v1/documentation/latest/guide/retries.html#standard-retry-mode AWS_RETRY_MODE: Final = "standard" # base 2 exponential backoff with max of 20 seconds: AWS_MAX_ATTEMPTS = 3 From 98aa1c11c800c18e71befb27e541646971f0d530 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:57:44 -0700 Subject: [PATCH 066/109] Update settings.py --- src/registrar/config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index a76f87847..530de3af3 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -44,7 +44,7 @@ s3_key_service = AppEnv().get_service(name="getgov-s3") if s3_key_service and s3_key_service.credentials: secret_s3 = s3_key_service.credentials.get else: - secret_s3 = None + secret_s3 = env # # # ### # Values obtained externally # # # # ### From 5b4960268417b9df879b1eeb9e578001d3b14824 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:15:11 -0700 Subject: [PATCH 067/109] Update workflow --- .github/workflows/daily-csv-upload.yaml | 8 ++++---- src/registrar/tests/test_reports.py | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 5925cffcb..bcec795d2 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -66,20 +66,20 @@ jobs: CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: - name: Generate current-federal.csv - uses: 18f/cg-deploy-action@main + uses: cloud-gov/cg-cli-tools@main with: cf_username: ${{ secrets[env.CF_USERNAME] }} cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} - full_command: "cf run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" + cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" - name: Generate current-full.csv - uses: 18f/cg-deploy-action@main + uses: cloud-gov/cg-cli-tools@main with: cf_username: ${{ secrets[env.CF_USERNAME] }} cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} - full_command: "cf run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_full_report' --name full" + cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_full_report' --name full" diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 5fe12fa37..c0c520955 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -9,7 +9,7 @@ from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command from unittest.mock import call, mock_open, patch from api.views import get_current_federal, get_current_full - +import boto3_mocking # type: ignore class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" @@ -84,6 +84,7 @@ class CsvReportsTest(TestCase): error = err.exception self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-full.csv'") + @boto3_mocking.patching def test_generate_federal_report(self): """Ensures that we correctly generate current-federal.csv""" expected_file_content = [ @@ -100,6 +101,7 @@ class CsvReportsTest(TestCase): # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) + @boto3_mocking.patching def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" expected_file_content = [ @@ -116,7 +118,7 @@ class CsvReportsTest(TestCase): content = fake_open() # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) - + def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" response = self.client.get("/api/v1/get-report/current-full") @@ -134,7 +136,8 @@ class CsvReportsTest(TestCase): self.assertEqual(response.status_code, 404) # Check that the response body contains "File not found" self.assertEqual(response.content.decode(), "File not found") - + + @boto3_mocking.patching def test_load_federal_report(self): """Tests the current-federal api link""" request = self.factory.get("/fake-path") @@ -152,6 +155,7 @@ class CsvReportsTest(TestCase): self.assertEqual(file_content, expected_file_content) + @boto3_mocking.patching def test_load_full_report(self): """Tests the current-federal api link""" request = self.factory.get("/fake-path") From d159ac09bf8f2ba2a10fafb3e08f69f8d94ce654 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:17:30 -0700 Subject: [PATCH 068/109] Fix typo --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index bcec795d2..558b9e0b5 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -72,7 +72,7 @@ jobs: cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} - cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" + cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" - name: Generate current-full.csv uses: cloud-gov/cg-cli-tools@main From e21b8b6e7149ae95cc719ced073866e8e0bca2d2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:28:29 -0700 Subject: [PATCH 069/109] Change environment default --- .github/workflows/daily-csv-upload.yaml | 2 +- src/registrar/config/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 558b9e0b5..27680f561 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -40,7 +40,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; core.setOutput('environment', environment); wait-for-deploy: diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 530de3af3..d23326553 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -38,7 +38,7 @@ if key_service and key_service.credentials: else: secret = env -# Get secrets from Cloud.gov user provided s3 service, if exists +# Get secrets from Cloud.gov user provided s3 service, if it exists # If not, get secrets from environment variables. s3_key_service = AppEnv().get_service(name="getgov-s3") if s3_key_service and s3_key_service.credentials: From 159118d005a8d321b270f96616103b16d3ebda82 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:21:33 -0700 Subject: [PATCH 070/109] Change how creds are handled --- .github/workflows/daily-csv-upload.yaml | 2 +- src/registrar/config/settings.py | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 27680f561..558b9e0b5 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -40,7 +40,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; core.setOutput('environment', environment); wait-for-deploy: diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index d23326553..9c4762e80 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -33,18 +33,20 @@ env = environs.Env() # Get secrets from Cloud.gov user provided service, if exists # If not, get secrets from environment variables key_service = AppEnv().get_service(name="getgov-credentials") + + +# Get secrets from Cloud.gov user provided s3 service, if it exists +s3_key_service = AppEnv().get_service(name="getgov-s3") + if key_service and key_service.credentials: + if s3_key_service and s3_key_service.credentials: + # Concatenate the credentials from our S3 service into our secret service + key_service.credentials.update(s3_key_service.credentials) secret = key_service.credentials.get else: secret = env -# Get secrets from Cloud.gov user provided s3 service, if it exists -# If not, get secrets from environment variables. -s3_key_service = AppEnv().get_service(name="getgov-s3") -if s3_key_service and s3_key_service.credentials: - secret_s3 = s3_key_service.credentials.get -else: - secret_s3 = env + # # # ### # Values obtained externally # # # # ### @@ -65,10 +67,11 @@ secret_key = secret("DJANGO_SECRET_KEY") secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None) secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None) -aws_s3_region_name = secret_s3("region", None) -secret_aws_s3_key_id = secret_s3("access_key_id", None) -secret_aws_s3_key = secret_s3("secret_access_key", None) -secret_aws_s3_bucket_name = secret_s3("bucket", None) +# TODO - allow for local env variable +aws_s3_region_name = secret("region", None) +secret_aws_s3_key_id = secret("access_key_id", None) +secret_aws_s3_key = secret("secret_access_key", None) +secret_aws_s3_bucket_name = secret("bucket", None) secret_registry_cl_id = secret("REGISTRY_CL_ID") secret_registry_password = secret("REGISTRY_PASSWORD") From bb175bd3d460ed507f414c0c9a8be1e633e3c324 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:27:27 -0700 Subject: [PATCH 071/109] Fix credentials not loading correctly --- src/docker-compose.yml | 5 +++++ src/registrar/config/settings.py | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 90ce1acb0..c9b78fd8e 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -51,6 +51,11 @@ services: # AWS credentials - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY + # AWS S3 bucket credentials + - AWS_S3_ACCESS_KEY_ID + - AWS_S3_SECRET_ACCESS_KEY + - AWS_S3_REGION + - AWS_S3_BUCKET_NAME stdin_open: true tty: true ports: diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 9c4762e80..b31590c4b 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -67,11 +67,11 @@ secret_key = secret("DJANGO_SECRET_KEY") secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None) secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None) -# TODO - allow for local env variable -aws_s3_region_name = secret("region", None) -secret_aws_s3_key_id = secret("access_key_id", None) -secret_aws_s3_key = secret("secret_access_key", None) -secret_aws_s3_bucket_name = secret("bucket", None) +# These keys are present in a getgov-s3 instance, or they can be defined locally +aws_s3_region_name = secret("region", None) or secret("AWS_S3_REGION", None) +secret_aws_s3_key_id = secret("access_key_id", None) or secret("AWS_S3_ACCESS_KEY_ID", None) +secret_aws_s3_key = secret("secret_access_key", None) or secret("AWS_S3_SECRET_ACCESS_KEY", None) +secret_aws_s3_bucket_name = secret("bucket", None) or secret("AWS_S3_BUCKET_NAME", None) secret_registry_cl_id = secret("REGISTRY_CL_ID") secret_registry_password = secret("REGISTRY_PASSWORD") From 72ef5bb0b1783e80a909082c468def469930e308 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:13:06 -0700 Subject: [PATCH 072/109] Test --- .github/workflows/daily-csv-upload.yaml | 2 +- src/registrar/tests/test_reports.py | 15 ++++++++++++--- src/registrar/utility/s3_bucket.py | 11 ++--------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 558b9e0b5..245f19fcc 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -32,7 +32,7 @@ on: jobs: variables: outputs: - environment: ${{ steps.var.outputs.environment }} + environment: 'za' runs-on: "ubuntu-latest" steps: - name: Setting global variables diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index c0c520955..b3cbb72fd 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -7,7 +7,7 @@ from registrar.models.user import User from django.contrib.auth import get_user_model from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command -from unittest.mock import call, mock_open, patch +from unittest.mock import MagicMock, call, mock_open, patch from api.views import get_current_federal, get_current_full import boto3_mocking # type: ignore @@ -73,7 +73,7 @@ class CsvReportsTest(TestCase): call_command("generate_current_federal_report") error = err.exception self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-federal.csv'") - + def test_create_failed_full(self): """Ensures that we return an error when we cannot find our created file""" fake_open = mock_open() @@ -119,15 +119,22 @@ class CsvReportsTest(TestCase): # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) + @boto3_mocking.patching def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" - response = self.client.get("/api/v1/get-report/current-full") + mock_client = MagicMock() + mock_client_instance = mock_client.return_value + with boto3_mocking.clients.handler_for("s3", mock_client): + response = self.client.get("/api/v1/get-report/current-full") + call_args = mock_client_instance + args, kwargs = call_args # Check that the response has status code 404 self.assertEqual(response.status_code, 404) # Check that the response body contains "File not found" self.assertEqual(response.content.decode(), "File not found") + @boto3_mocking.patching def test_not_found_federal_report(self): """Ensures that we get a not found when the report doesn't exist""" response = self.client.get("/api/v1/get-report/current-federal") @@ -140,6 +147,8 @@ class CsvReportsTest(TestCase): @boto3_mocking.patching def test_load_federal_report(self): """Tests the current-federal api link""" + if not boto3_mocking.patching_engaged(): + raise Exception("test123") request = self.factory.get("/fake-path") response = get_current_federal(request, file_path="registrar/tests/data/fake_current_federal.csv") # Check that the response has status code 200 diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py index 9e7f0da31..329a68c40 100644 --- a/src/registrar/utility/s3_bucket.py +++ b/src/registrar/utility/s3_bucket.py @@ -23,20 +23,12 @@ class S3ClientHelper: config=settings.BOTO_CONFIG, ) except Exception as exc: - raise S3ClientError("Could not access the S3 client.") from exc + raise S3ClientError("Could not access the S3 client") from exc def get_bucket_name(self): """Gets the name of our S3 Bucket""" return settings.AWS_S3_BUCKET_NAME - def list_objects(self): - """Returns a list of the top 1000 objects within our S3 instance""" - try: - response = self.boto_client.list_objects_v2(Bucket=self.get_bucket_name()) - except Exception as exc: - raise S3ClientError("Couldn't list objects") from exc - return response - def upload_file(self, file_path, file_name): """Uploads a file to our S3 instance""" try: @@ -53,6 +45,7 @@ class S3ClientHelper: raise S3ClientError("File was not found") from exc except Exception as exc: raise S3ClientError("Couldn't get file, an unspecified error occured") from exc + file_content = response["Body"].read() if decode_to_utf: return file_content.decode("utf-8") From d9d86c2eb8c8e2c572ab830846ac810b52ce8c06 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:33:15 -0700 Subject: [PATCH 073/109] Update test_reports.py --- src/registrar/tests/test_reports.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index b3cbb72fd..e8796e1fa 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -9,6 +9,8 @@ from registrar.utility.csv_export import export_domains_to_writer from django.core.management import call_command from unittest.mock import MagicMock, call, mock_open, patch from api.views import get_current_federal, get_current_full +from django.conf import settings +from botocore.exceptions import ClientError import boto3_mocking # type: ignore class CsvReportsTest(TestCase): @@ -122,13 +124,23 @@ class CsvReportsTest(TestCase): @boto3_mocking.patching def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" + def side_effect(fake): + raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object") mock_client = MagicMock() mock_client_instance = mock_client.return_value - with boto3_mocking.clients.handler_for("s3", mock_client): - response = self.client.get("/api/v1/get-report/current-full") + mock_client.get_object.side_effect = side_effect + with patch('boto3.client', return_value=mock_client): + with self.assertRaises(ClientError) as context: + with boto3_mocking.clients.handler_for("s3", mock_client): + response = self.client.get("/api/v1/get-report/current-full") - call_args = mock_client_instance - args, kwargs = call_args + expected_call = [ + call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key='current-full.csv') + ] + mock_client_instance.assert_has_calls(expected_call) + mock_client_instance.get_object.side_effect = Exception("An error occurred") + print("look") + print(response.content.decode()) # Check that the response has status code 404 self.assertEqual(response.status_code, 404) # Check that the response body contains "File not found" From 55126689331a4d2e090b6ab3e8e7495a90ffa17a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:48:41 -0700 Subject: [PATCH 074/109] Test cases - WIP --- src/registrar/tests/test_reports.py | 115 +++++++++++++++++++--------- src/registrar/utility/s3_bucket.py | 48 ++++++++++-- 2 files changed, 119 insertions(+), 44 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index e8796e1fa..23ac04139 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -1,4 +1,5 @@ import csv +import io from django.test import Client, RequestFactory, TestCase from io import StringIO from registrar.models.domain_information import DomainInformation @@ -11,7 +12,8 @@ from unittest.mock import MagicMock, call, mock_open, patch from api.views import get_current_federal, get_current_full from django.conf import settings from botocore.exceptions import ClientError -import boto3_mocking # type: ignore +import boto3_mocking +from registrar.utility.s3_bucket import S3ClientError, S3ClientErrorCodes # type: ignore class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" @@ -124,75 +126,114 @@ class CsvReportsTest(TestCase): @boto3_mocking.patching def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" - def side_effect(fake): + def side_effect(Bucket, Key): raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object") mock_client = MagicMock() - mock_client_instance = mock_client.return_value mock_client.get_object.side_effect = side_effect - with patch('boto3.client', return_value=mock_client): - with self.assertRaises(ClientError) as context: - with boto3_mocking.clients.handler_for("s3", mock_client): - response = self.client.get("/api/v1/get-report/current-full") - expected_call = [ - call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key='current-full.csv') - ] - mock_client_instance.assert_has_calls(expected_call) - mock_client_instance.get_object.side_effect = Exception("An error occurred") - print("look") - print(response.content.decode()) - # Check that the response has status code 404 - self.assertEqual(response.status_code, 404) - # Check that the response body contains "File not found" - self.assertEqual(response.content.decode(), "File not found") + response = None + with boto3_mocking.clients.handler_for("s3", mock_client): + with patch('boto3.client', return_value=mock_client): + with self.assertRaises(S3ClientError) as context: + response = self.client.get("/api/v1/get-report/current-full") + # Check that the response has status code 500 + self.assertEqual(response.status_code, 500) + + # Check that we get the right error back from the page + self.assertEqual(context.exception.code, S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) + @boto3_mocking.patching def test_not_found_federal_report(self): """Ensures that we get a not found when the report doesn't exist""" - response = self.client.get("/api/v1/get-report/current-federal") + def side_effect(Bucket, Key): + raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object") + mock_client = MagicMock() + mock_client.get_object.side_effect = side_effect + + with boto3_mocking.clients.handler_for("s3", mock_client): + with patch('boto3.client', return_value=mock_client): + with self.assertRaises(S3ClientError) as context: + response = self.client.get("/api/v1/get-report/current-federal") + # Check that the response has status code 500 + self.assertEqual(response.status_code, 500) - # Check that the response has status code 404 - self.assertEqual(response.status_code, 404) - # Check that the response body contains "File not found" - self.assertEqual(response.content.decode(), "File not found") + # Check that we get the right error back from the page + self.assertEqual(context.exception.code, S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) @boto3_mocking.patching def test_load_federal_report(self): - """Tests the current-federal api link""" - if not boto3_mocking.patching_engaged(): - raise Exception("test123") - request = self.factory.get("/fake-path") - response = get_current_federal(request, file_path="registrar/tests/data/fake_current_federal.csv") + """Tests the get_current_federal api endpoint""" + self.maxDiff = None + mock_client = MagicMock() + mock_client_instance = mock_client.return_value + + with open("registrar/tests/data/fake_current_federal.csv", "r") as file: + file_content = file.read() + + # Mock a recieved file + mock_client_instance.get_object.return_value = { + 'Body': io.BytesIO(file_content.encode()) + } + with boto3_mocking.clients.handler_for("s3", mock_client): + request = self.factory.get("/fake-path") + response = get_current_federal(request) + + # Check that we are sending the correct calls. + # Ensures that we are decoding the file content recieved from AWS. + expected_call = [ + call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key='current-federal.csv') + ] + mock_client_instance.assert_has_calls(expected_call) + # Check that the response has status code 200 self.assertEqual(response.status_code, 200) - # Check that the response contains what we expect - file_content = b"".join(response.streaming_content).decode("utf-8") + # Check that the response contains what we expect expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,,,," - ) + ).encode() - self.assertEqual(file_content, expected_file_content) + self.assertEqual(expected_file_content, response.content) @boto3_mocking.patching def test_load_full_report(self): """Tests the current-federal api link""" - request = self.factory.get("/fake-path") - response = get_current_full(request, file_path="registrar/tests/data/fake_current_full.csv") + mock_client = MagicMock() + mock_client_instance = mock_client.return_value + + with open("registrar/tests/data/fake_current_full.csv", "r") as file: + file_content = file.read() + + # Mock a recieved file + mock_client_instance.get_object.return_value = { + 'Body': io.BytesIO(file_content.encode()) + } + with boto3_mocking.clients.handler_for("s3", mock_client): + request = self.factory.get("/fake-path") + response = get_current_federal(request) + + # Check that we are sending the correct calls. + # Ensures that we are decoding the file content recieved from AWS. + expected_call = [ + call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key='current-federal.csv') + ] + mock_client_instance.assert_has_calls(expected_call) + # Check that the response has status code 200 self.assertEqual(response.status_code, 200) + # Check that the response contains what we expect - file_content = b"".join(response.streaming_content).decode("utf-8") expected_file_content = ( "Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\n" "adomain2.gov,Interstate,,,,," - ) + ).encode() - self.assertEqual(file_content, expected_file_content) + self.assertEqual(expected_file_content, response.content) class ExportDataTest(TestCase): diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py index 329a68c40..6370b0736 100644 --- a/src/registrar/utility/s3_bucket.py +++ b/src/registrar/utility/s3_bucket.py @@ -1,14 +1,45 @@ """Utilities for accessing an AWS S3 bucket""" +from enum import IntEnum import boto3 - +from botocore.exceptions import ClientError from django.conf import settings from django.template.loader import get_template +class S3ClientErrorCodes(IntEnum): + """Used for S3ClientError + Error code overview: + - 1 ACCESS_S3_CLIENT_ERROR + - 2 UPLOAD_FILE_ERROR + - 3 FILE_NOT_FOUND_ERROR + - 4 GET_FILE_ERROR + """ + + ACCESS_S3_CLIENT_ERROR = 1 + UPLOAD_FILE_ERROR = 2 + FILE_NOT_FOUND_ERROR = 3 + GET_FILE_ERROR = 4 class S3ClientError(RuntimeError): """Local error for handling all failures with boto3.client""" - pass + _error_mapping = { + S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR: "Failed to establish a connection with the storage service.", + S3ClientErrorCodes.UPLOAD_FILE_ERROR: "File upload to the storage service failed.", + S3ClientErrorCodes.FILE_NOT_FOUND_ERROR: "Requested file not found in the storage service.", + S3ClientErrorCodes.GET_FILE_ERROR: ( + "Retrieval of the requested file from " + "the storage service failed due to an unspecified error." + ), + } + + def __init__(self, *args, code=None, **kwargs): + super().__init__(*args, **kwargs) + self.code = code + if self.code in self._error_mapping: + self.message = self._error_mapping.get(self.code) + + def __str__(self): + return f"{self.message}" class S3ClientHelper: @@ -23,7 +54,7 @@ class S3ClientHelper: config=settings.BOTO_CONFIG, ) except Exception as exc: - raise S3ClientError("Could not access the S3 client") from exc + raise S3ClientError(code=S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR) from exc def get_bucket_name(self): """Gets the name of our S3 Bucket""" @@ -34,17 +65,20 @@ class S3ClientHelper: try: response = self.boto_client.upload_file(file_path, self.get_bucket_name(), file_name) except Exception as exc: - raise S3ClientError("Couldn't upload file") from exc + raise S3ClientError(code=S3ClientErrorCodes.UPLOAD_FILE_ERROR) from exc return response def get_file(self, file_name, decode_to_utf=False): """Gets a file to our S3 instance and returns the file content""" try: response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name) - except self.boto_client.exceptions.NoSuchKey as exc: - raise S3ClientError("File was not found") from exc + except ClientError as exc: + if exc.response['Error']['Code'] == 'NoSuchKey': + raise S3ClientError(code=S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) from exc + else: + raise S3ClientError(code=S3ClientErrorCodes.GET_FILE_ERROR) from exc except Exception as exc: - raise S3ClientError("Couldn't get file, an unspecified error occured") from exc + raise S3ClientError(code=S3ClientErrorCodes.GET_FILE_ERROR) from exc file_content = response["Body"].read() if decode_to_utf: From 7f71eb4b96712675fc88cd8cd99ea55ce835e461 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 08:52:50 -0700 Subject: [PATCH 075/109] Update test cases --- src/registrar/tests/test_reports.py | 36 ++++++++--------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 23ac04139..a06d6f1c2 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -68,39 +68,21 @@ class CsvReportsTest(TestCase): User.objects.all().delete() super().tearDown() - def test_create_failed_federal(self): - """Ensures that we return an error when we cannot find our created file""" - fake_open = mock_open() - # We don't actually want to write anything for a test case, - # we just want to verify what is being written. - with patch("builtins.open", fake_open), self.assertRaises(FileNotFoundError) as err: - call_command("generate_current_federal_report") - error = err.exception - self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-federal.csv'") - - def test_create_failed_full(self): - """Ensures that we return an error when we cannot find our created file""" - fake_open = mock_open() - # We don't actually want to write anything for a test case, - # we just want to verify what is being written. - with patch("builtins.open", fake_open), self.assertRaises(FileNotFoundError) as err: - call_command("generate_current_full_report") - error = err.exception - self.assertEqual(str(error), "Could not find newly created file at 'migrationdata/current-full.csv'") - @boto3_mocking.patching def test_generate_federal_report(self): """Ensures that we correctly generate current-federal.csv""" + mock_client = MagicMock() + fake_open = mock_open() expected_file_content = [ call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), ] - fake_open = mock_open() # We don't actually want to write anything for a test case, # we just want to verify what is being written. - with patch("builtins.open", fake_open): - call_command("generate_current_federal_report", checkpath=False) + with boto3_mocking.clients.handler_for("s3", mock_client): + with patch("builtins.open", fake_open): + call_command("generate_current_federal_report", checkpath=False) content = fake_open() # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) @@ -108,17 +90,19 @@ class CsvReportsTest(TestCase): @boto3_mocking.patching def test_generate_full_report(self): """Ensures that we correctly generate current-full.csv""" + mock_client = MagicMock() + fake_open = mock_open() expected_file_content = [ call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"), call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("adomain2.gov,Interstate,,,,, \r\n"), ] - fake_open = mock_open() # We don't actually want to write anything for a test case, # we just want to verify what is being written. - with patch("builtins.open", fake_open): - call_command("generate_current_full_report", checkpath=False) + with boto3_mocking.clients.handler_for("s3", mock_client): + with patch("builtins.open", fake_open): + call_command("generate_current_full_report", checkpath=False) content = fake_open() # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) From ea289a6f592a8e6009b09fd8ca5083dc5442d3bb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:06:27 -0700 Subject: [PATCH 076/109] Linting + test cases --- src/api/views.py | 3 +- .../generate_current_federal_report.py | 6 +-- .../commands/generate_current_full_report.py | 7 ++-- src/registrar/tests/test_reports.py | 42 +++++++++---------- src/registrar/utility/s3_bucket.py | 10 +++-- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 60f11023e..f888ee6d4 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,8 +1,7 @@ """Internal API views""" -import os from django.apps import apps from django.views.decorators.http import require_http_methods -from django.http import FileResponse, HttpResponse, JsonResponse +from django.http import HttpResponse, JsonResponse from django.utils.safestring import mark_safe from registrar.templatetags.url_helpers import public_site_url diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 6075d8ebb..478265ca0 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -39,11 +39,11 @@ class Command(BaseCommand): logger.info(f"Success! Created {file_name}") def generate_current_federal_report(self, directory, file_name, check_path): - """Creates a current-full.csv file under the specified directory, + """Creates a current-full.csv file under the specified directory, then uploads it to a AWS S3 bucket""" s3_client = S3ClientHelper() file_path = os.path.join(directory, file_name) - + # Generate a file locally for upload with open(file_path, "w") as file: csv_export.export_data_federal_to_csv(file) @@ -51,5 +51,5 @@ class Command(BaseCommand): if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") - # Upload this generated file for our S3 instance + # Upload this generated file for our S3 instance s3_client.upload_file(file_path, file_name) diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index 83be4f9b5..c41c697a1 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -39,11 +39,11 @@ class Command(BaseCommand): logger.info(f"Success! Created {file_name}") def generate_current_full_report(self, directory, file_name, check_path): - """Creates a current-full.csv file under the specified directory, + """Creates a current-full.csv file under the specified directory, then uploads it to a AWS S3 bucket""" s3_client = S3ClientHelper() file_path = os.path.join(directory, file_name) - + # Generate a file locally for upload with open(file_path, "w") as file: csv_export.export_data_full_to_csv(file) @@ -51,6 +51,5 @@ class Command(BaseCommand): if check_path and not os.path.exists(file_path): raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") - # Upload this generated file for our S3 instance + # Upload this generated file for our S3 instance s3_client.upload_file(file_path, file_name) - diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index a06d6f1c2..266f7c799 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -15,6 +15,7 @@ from botocore.exceptions import ClientError import boto3_mocking from registrar.utility.s3_bucket import S3ClientError, S3ClientErrorCodes # type: ignore + class CsvReportsTest(TestCase): """Tests to determine if we are uploading our reports correctly""" @@ -106,18 +107,20 @@ class CsvReportsTest(TestCase): content = fake_open() # Now you can make assertions about how you expect 'file' to be used. content.write.assert_has_calls(expected_file_content) - + @boto3_mocking.patching def test_not_found_full_report(self): """Ensures that we get a not found when the report doesn't exist""" + def side_effect(Bucket, Key): raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object") + mock_client = MagicMock() mock_client.get_object.side_effect = side_effect response = None with boto3_mocking.clients.handler_for("s3", mock_client): - with patch('boto3.client', return_value=mock_client): + with patch("boto3.client", return_value=mock_client): with self.assertRaises(S3ClientError) as context: response = self.client.get("/api/v1/get-report/current-full") # Check that the response has status code 500 @@ -126,17 +129,18 @@ class CsvReportsTest(TestCase): # Check that we get the right error back from the page self.assertEqual(context.exception.code, S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) - @boto3_mocking.patching def test_not_found_federal_report(self): """Ensures that we get a not found when the report doesn't exist""" + def side_effect(Bucket, Key): raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object") + mock_client = MagicMock() mock_client.get_object.side_effect = side_effect - + with boto3_mocking.clients.handler_for("s3", mock_client): - with patch('boto3.client', return_value=mock_client): + with patch("boto3.client", return_value=mock_client): with self.assertRaises(S3ClientError) as context: response = self.client.get("/api/v1/get-report/current-federal") # Check that the response has status code 500 @@ -144,7 +148,7 @@ class CsvReportsTest(TestCase): # Check that we get the right error back from the page self.assertEqual(context.exception.code, S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) - + @boto3_mocking.patching def test_load_federal_report(self): """Tests the get_current_federal api endpoint""" @@ -156,18 +160,14 @@ class CsvReportsTest(TestCase): file_content = file.read() # Mock a recieved file - mock_client_instance.get_object.return_value = { - 'Body': io.BytesIO(file_content.encode()) - } + mock_client_instance.get_object.return_value = {"Body": io.BytesIO(file_content.encode())} with boto3_mocking.clients.handler_for("s3", mock_client): request = self.factory.get("/fake-path") response = get_current_federal(request) - - # Check that we are sending the correct calls. + + # Check that we are sending the correct calls. # Ensures that we are decoding the file content recieved from AWS. - expected_call = [ - call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key='current-federal.csv') - ] + expected_call = [call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key="current-federal.csv")] mock_client_instance.assert_has_calls(expected_call) # Check that the response has status code 200 @@ -192,18 +192,14 @@ class CsvReportsTest(TestCase): file_content = file.read() # Mock a recieved file - mock_client_instance.get_object.return_value = { - 'Body': io.BytesIO(file_content.encode()) - } + mock_client_instance.get_object.return_value = {"Body": io.BytesIO(file_content.encode())} with boto3_mocking.clients.handler_for("s3", mock_client): request = self.factory.get("/fake-path") - response = get_current_federal(request) - - # Check that we are sending the correct calls. + response = get_current_full(request) + + # Check that we are sending the correct calls. # Ensures that we are decoding the file content recieved from AWS. - expected_call = [ - call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key='current-federal.csv') - ] + expected_call = [call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key="current-full.csv")] mock_client_instance.assert_has_calls(expected_call) # Check that the response has status code 200 diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py index 6370b0736..162be37c9 100644 --- a/src/registrar/utility/s3_bucket.py +++ b/src/registrar/utility/s3_bucket.py @@ -4,7 +4,7 @@ from enum import IntEnum import boto3 from botocore.exceptions import ClientError from django.conf import settings -from django.template.loader import get_template + class S3ClientErrorCodes(IntEnum): """Used for S3ClientError @@ -20,15 +20,16 @@ class S3ClientErrorCodes(IntEnum): FILE_NOT_FOUND_ERROR = 3 GET_FILE_ERROR = 4 + class S3ClientError(RuntimeError): """Local error for handling all failures with boto3.client""" + _error_mapping = { S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR: "Failed to establish a connection with the storage service.", S3ClientErrorCodes.UPLOAD_FILE_ERROR: "File upload to the storage service failed.", S3ClientErrorCodes.FILE_NOT_FOUND_ERROR: "Requested file not found in the storage service.", S3ClientErrorCodes.GET_FILE_ERROR: ( - "Retrieval of the requested file from " - "the storage service failed due to an unspecified error." + "Retrieval of the requested file from " "the storage service failed due to an unspecified error." ), } @@ -44,6 +45,7 @@ class S3ClientError(RuntimeError): class S3ClientHelper: """Helper class that simplifies S3 intialization""" + def __init__(self): try: self.boto_client = boto3.client( @@ -73,7 +75,7 @@ class S3ClientHelper: try: response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name) except ClientError as exc: - if exc.response['Error']['Code'] == 'NoSuchKey': + if exc.response["Error"]["Code"] == "NoSuchKey": raise S3ClientError(code=S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) from exc else: raise S3ClientError(code=S3ClientErrorCodes.GET_FILE_ERROR) from exc From 840cd4748aeaa161f7fd2996e32cb9b16653f43b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:31:02 -0700 Subject: [PATCH 077/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 245f19fcc..ebd7c0df5 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -24,15 +24,14 @@ on: - bl - rjm - dk - # TODO - uncomment after #1403 is finished - #schedule: + schedule: # Runs every day at 5 AM UTC. - # - cron: "0 5 * * *" + - cron: "0 5 * * *" jobs: variables: outputs: - environment: 'za' + environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" steps: - name: Setting global variables @@ -60,7 +59,7 @@ jobs: upload-reports: runs-on: ubuntu-latest - needs: [variables, wait-for-deploy] + needs: [variables] env: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD From 18a10978e1c019e9bdacc999be557b9849b24345 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:33:56 -0700 Subject: [PATCH 078/109] Print all secrets --- .github/workflows/daily-csv-upload.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index ebd7c0df5..72a2c3c93 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -56,14 +56,16 @@ jobs: timeoutSeconds: 600 # the time to wait between checks, in seconds intervalSeconds: 10 - + upload-reports: runs-on: ubuntu-latest - needs: [variables] + needs: [variables, wait-for-deploy] env: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: + - name: Print names of all secrets + run: echo ${{ toJson(secrets) }} - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: From 20122727f76639a95e34b327ff6161aa02d267e5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:35:03 -0700 Subject: [PATCH 079/109] More prints --- .github/workflows/daily-csv-upload.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 72a2c3c93..21ef3676c 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -66,6 +66,10 @@ jobs: steps: - name: Print names of all secrets run: echo ${{ toJson(secrets) }} + - name: Print cf_username + run: echo ${{ env.CF_USERNAME }} + - name: Print cf_password + run: echo ${{ env.CF_PASSWORD }} - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: From 6a10b8096dca975130842dba38123d3dc6ec3eee Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:42:28 -0700 Subject: [PATCH 080/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 21ef3676c..0cbd44f0d 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -59,17 +59,21 @@ jobs: upload-reports: runs-on: ubuntu-latest - needs: [variables, wait-for-deploy] + needs: [variables] env: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: - - name: Print names of all secrets - run: echo ${{ toJson(secrets) }} - - name: Print cf_username - run: echo ${{ env.CF_USERNAME }} - - name: Print cf_password - run: echo ${{ env.CF_PASSWORD }} + - name: Check secrets + run: | + if [[ -z "${{ secrets.CF_ZA_USERNAME }}" ]]; then + echo "CF_ZA_USERNAME is not set" + exit 1 + fi + if [[ -z "${{ secrets.CF_ZA_PASSWORD }}" ]]; then + echo "CF_ZA_PASSWORD is not set" + exit 1 + fi - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: From 89f45ef2fb7fe69b287c4e869df8a7e3b9b31cec Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:59:06 -0700 Subject: [PATCH 081/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 0cbd44f0d..886808528 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -64,16 +64,16 @@ jobs: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: - - name: Check secrets - run: | - if [[ -z "${{ secrets.CF_ZA_USERNAME }}" ]]; then - echo "CF_ZA_USERNAME is not set" - exit 1 - fi - if [[ -z "${{ secrets.CF_ZA_PASSWORD }}" ]]; then - echo "CF_ZA_PASSWORD is not set" - exit 1 - fi + - name: Check secrets + run: | + if [[ -z "${{ secrets.CF_ZA_USERNAME }}" ]]; then + echo "CF_ZA_USERNAME is not set" + exit 1 + fi + if [[ -z "${{ secrets.CF_ZA_PASSWORD }}" ]]; then + echo "CF_ZA_PASSWORD is not set" + exit 1 + fi - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: From 5c3766cb0636a2dcf66bc59fd5d14cdcfb0a9510 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:21:02 -0700 Subject: [PATCH 082/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 886808528..7421a2ae7 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -39,7 +39,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'za'; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; core.setOutput('environment', environment); wait-for-deploy: @@ -64,16 +64,6 @@ jobs: CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD steps: - - name: Check secrets - run: | - if [[ -z "${{ secrets.CF_ZA_USERNAME }}" ]]; then - echo "CF_ZA_USERNAME is not set" - exit 1 - fi - if [[ -z "${{ secrets.CF_ZA_PASSWORD }}" ]]; then - echo "CF_ZA_PASSWORD is not set" - exit 1 - fi - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: From 23b30808490141241642f489e262cf1b8db9a1e7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:24:32 -0700 Subject: [PATCH 083/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 7421a2ae7..25ad0c074 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -67,8 +67,8 @@ jobs: - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: - cf_username: ${{ secrets[env.CF_USERNAME] }} - cf_password: ${{ secrets[env.CF_PASSWORD] }} + cf_username: ${{ secrets[CF_ZA_USERNAME] }} + cf_password: ${{ secrets[CF_ZA_PASSWORD] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" From 873f2df7e0464b65afb4b27932e0b1e7eb15f970 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:38:19 -0700 Subject: [PATCH 084/109] Test string --- .github/workflows/daily-csv-upload.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 25ad0c074..ce60b75ac 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -67,8 +67,8 @@ jobs: - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: - cf_username: ${{ secrets[CF_ZA_USERNAME] }} - cf_password: ${{ secrets[CF_ZA_PASSWORD] }} + cf_username: ${{ secrets["CF_ZA_USERNAME"] }} + cf_password: ${{ secrets["CF_ZA_USERNAME"] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" From bdbde4a56b152bfea1da405f563f1610f71992d6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:39:23 -0700 Subject: [PATCH 085/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index ce60b75ac..2b00215a4 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -68,7 +68,7 @@ jobs: uses: cloud-gov/cg-cli-tools@main with: cf_username: ${{ secrets["CF_ZA_USERNAME"] }} - cf_password: ${{ secrets["CF_ZA_USERNAME"] }} + cf_password: ${{ secrets["CF_ZA_PASSWORD"] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" From 5ce598e848c81172b2480abb2e2a66f7f3278ebe Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:42:28 -0700 Subject: [PATCH 086/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 2b00215a4..7421a2ae7 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -67,8 +67,8 @@ jobs: - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: - cf_username: ${{ secrets["CF_ZA_USERNAME"] }} - cf_password: ${{ secrets["CF_ZA_PASSWORD"] }} + cf_username: ${{ secrets[env.CF_USERNAME] }} + cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov cf_space: ${{ needs.variables.outputs.environment }} cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" From d4224799d82f3d8482d629caf168228602c64404 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:44:15 -0700 Subject: [PATCH 087/109] Remove wait-for-deploy and added comment --- .github/workflows/daily-csv-upload.yaml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 7421a2ae7..321fd92a7 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,6 +2,7 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: + # pull_request will be removed when merged pull_request: workflow_dispatch: inputs: @@ -42,21 +43,6 @@ jobs: const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; core.setOutput('environment', environment); - wait-for-deploy: - runs-on: ubuntu-latest - steps: - - name: Wait for deploy to complete - uses: fountainhead/action-wait-for-check@v1.0.0 - id: wait-for-deploy - with: - token: ${{ secrets.GITHUB_TOKEN }} - checkName: "deploy" - ref: ${{ github.event.pull_request.head.sha }} - # the maximum time to wait for the check to complete, in seconds - timeoutSeconds: 600 - # the time to wait between checks, in seconds - intervalSeconds: 10 - upload-reports: runs-on: ubuntu-latest needs: [variables] From aad157c7e117132e4536df729db8eba9dc0d73c1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:49:23 -0700 Subject: [PATCH 088/109] Change to double quotes --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 321fd92a7..a9f82d096 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -40,7 +40,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : 'ZA'; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : "ZA"; core.setOutput('environment', environment); upload-reports: From b81a3f1708178f792e97bc70f43eb1fc4db0c3b5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:51:52 -0700 Subject: [PATCH 089/109] Add missing dollar sign --- .github/workflows/daily-csv-upload.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index a9f82d096..3b9918713 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -47,8 +47,8 @@ jobs: runs-on: ubuntu-latest needs: [variables] env: - CF_USERNAME: CF_{{ needs.variables.outputs.environment }}_USERNAME - CF_PASSWORD: CF_{{ needs.variables.outputs.environment }}_PASSWORD + CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME + CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD steps: - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main From 96ab2bc75825c41fdb9cbc6e825ae92703beeea3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:47:55 -0700 Subject: [PATCH 090/109] Add readme instructions --- docs/developer/README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/developer/README.md b/docs/developer/README.md index 9cfdb2149..5595b627b 100644 --- a/docs/developer/README.md +++ b/docs/developer/README.md @@ -295,7 +295,7 @@ sudo sntp -sS time.nist.gov ``` ## Connection pool -To handle our connection to the registry, we utilize a connection pool to keep a socket open to increase responsiveness. In order to accomplish this, we are utilizing a heavily modified version of the (geventconnpool)[https://github.com/rasky/geventconnpool] library. +To handle our connection to the registry, we utilize a connection pool to keep a socket open to increase responsiveness. In order to accomplish this, we are utilizing a heavily modified version of the [geventconnpool](https://github.com/rasky/geventconnpool) library. ### Settings The config for the connection pool exists inside the `settings.py` file. @@ -319,4 +319,19 @@ Our connection pool has a built-in `pool_status` object which you can call at an 5. `print(registry.pool_status.connection_success)` * Should return true -If you have multiple instances (staging for example), then repeat commands 1-5 for each instance you want to test. \ No newline at end of file +If you have multiple instances (staging for example), then repeat commands 1-5 for each instance you want to test. + +## Adding a S3 instance to your sandbox +This can either be done through the CLI, or through the cloud.gov dashboard. Generally, it is better to do it through the dashboard as it handles app binding for you. + +To associate a S3 instance to your sandbox, follow these steps: +1. Navigate to https://dashboard.fr.cloud.gov/login +2. Select your sandbox from the `Applications` tab +3. Click `Services` on the application nav bar +4. Add a new service (plus symbol) +5. Click `Marketplace Service` +6. On the `Select the service` dropdown, select `s3` +7. Under the dropdown on `Select Plan`, select `basic-sandbox` +8. Under `Service Instance` enter `getgov-s3` for the name + +See this [resource](https://cloud.gov/docs/services/s3/) for information on associating an S3 instance with your sandbox through the CLI. \ No newline at end of file From d2701cc18da9911f5f776abb3b470251f08c9bba Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:15:12 -0700 Subject: [PATCH 091/109] Add better comments --- .../generate_current_federal_report.py | 5 +- .../commands/generate_current_full_report.py | 5 +- src/registrar/utility/s3_bucket.py | 70 +++++++++++++++++-- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/registrar/management/commands/generate_current_federal_report.py b/src/registrar/management/commands/generate_current_federal_report.py index 478265ca0..1a123bf5b 100644 --- a/src/registrar/management/commands/generate_current_federal_report.py +++ b/src/registrar/management/commands/generate_current_federal_report.py @@ -11,7 +11,10 @@ logger = logging.getLogger(__name__) class Command(BaseCommand): - help = "" + help = ( + "Generates and uploads a current-federal.csv file to our S3 bucket " + "which is based off of all existing federal Domains." + ) def add_arguments(self, parser): """Add our two filename arguments.""" diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index c41c697a1..7cf6fbb5a 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -11,7 +11,10 @@ logger = logging.getLogger(__name__) class Command(BaseCommand): - help = "" + help = ( + "Generates and uploads a current-full.csv file to our S3 bucket " + "which is based off of all existing Domains." + ) def add_arguments(self, parser): """Add our two filename arguments.""" diff --git a/src/registrar/utility/s3_bucket.py b/src/registrar/utility/s3_bucket.py index 162be37c9..f4e44b0b9 100644 --- a/src/registrar/utility/s3_bucket.py +++ b/src/registrar/utility/s3_bucket.py @@ -22,7 +22,17 @@ class S3ClientErrorCodes(IntEnum): class S3ClientError(RuntimeError): - """Local error for handling all failures with boto3.client""" + """ + Custom exception class for handling errors related to interactions with the S3 storage service via boto3.client. + + This class maps error codes to human-readable error messages. When an instance of S3ClientError is created, + an error code can be passed in to set the error message for that instance. + + Attributes: + _error_mapping: A dictionary mapping error codes to error messages. + code: The error code for a specific instance of S3ClientError. + message: The error message for a specific instance of S3ClientError, determined by the error code. + """ _error_mapping = { S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR: "Failed to establish a connection with the storage service.", @@ -44,7 +54,15 @@ class S3ClientError(RuntimeError): class S3ClientHelper: - """Helper class that simplifies S3 intialization""" + """ + A helper class for interacting with Amazon S3 via the boto3 client. + + This class simplifies the process of initializing the boto3 client, + uploading files to S3, and retrieving files from S3. + + Attributes: + boto_client: The boto3 client used to interact with S3. + """ def __init__(self): try: @@ -59,11 +77,35 @@ class S3ClientHelper: raise S3ClientError(code=S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR) from exc def get_bucket_name(self): - """Gets the name of our S3 Bucket""" + """ + Retrieves the name of the S3 bucket. + + This method returns the name of the S3 bucket as defined in the application's settings. + + Returns: + str: The name of the S3 bucket. + """ + return settings.AWS_S3_BUCKET_NAME def upload_file(self, file_path, file_name): - """Uploads a file to our S3 instance""" + """ + Uploads a file to the S3 bucket. + + This method attempts to upload a file to the S3 bucket using the boto3 client. + If an exception occurs during the upload process, it raises an S3ClientError with an UPLOAD_FILE_ERROR code. + + Args: + file_path (str): The path of the file to upload. + file_name (str): The name to give to the file in the S3 bucket. + + Returns: + dict: The response from the boto3 client's upload_file method. + + Raises: + S3ClientError: If the file cannot be uploaded to the S3 bucket. + """ + try: response = self.boto_client.upload_file(file_path, self.get_bucket_name(), file_name) except Exception as exc: @@ -71,7 +113,25 @@ class S3ClientHelper: return response def get_file(self, file_name, decode_to_utf=False): - """Gets a file to our S3 instance and returns the file content""" + """ + Retrieves a file from the S3 bucket and returns its content. + + This method attempts to retrieve a file from the S3 bucket using the boto3 client. + If the file is not found, it raises an S3ClientError with a FILE_NOT_FOUND_ERROR code. + For any other exceptions during the retrieval process, it raises an S3ClientError with a GET_FILE_ERROR code. + + Args: + file_name (str): The name of the file to retrieve from the S3 bucket. + decode_to_utf (bool, optional): If True, the file content is decoded from bytes to a UTF-8 string. + Defaults to False. + + Returns: + bytes or str: The content of the file. If decode_to_utf is True, this is a string. Otherwise, its bytes. + + Raises: + S3ClientError: If the file cannot be retrieved from the S3 bucket. + """ + try: response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name) except ClientError as exc: From a2bcc8b9c06efec1bd6c97ed70ce3a7e3363cfc4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:20:26 -0700 Subject: [PATCH 092/109] Linting pt. 2 --- .../management/commands/generate_current_full_report.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/management/commands/generate_current_full_report.py b/src/registrar/management/commands/generate_current_full_report.py index 7cf6fbb5a..80c031605 100644 --- a/src/registrar/management/commands/generate_current_full_report.py +++ b/src/registrar/management/commands/generate_current_full_report.py @@ -12,8 +12,7 @@ logger = logging.getLogger(__name__) class Command(BaseCommand): help = ( - "Generates and uploads a current-full.csv file to our S3 bucket " - "which is based off of all existing Domains." + "Generates and uploads a current-full.csv file to our S3 bucket " "which is based off of all existing Domains." ) def add_arguments(self, parser): From 1859d56ddf5b3f9937adac74cd001f67e92b0917 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:19:42 -0700 Subject: [PATCH 093/109] Update test_reports.py --- src/registrar/tests/test_reports.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 266f7c799..b94316248 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -85,7 +85,7 @@ class CsvReportsTest(TestCase): with patch("builtins.open", fake_open): call_command("generate_current_federal_report", checkpath=False) content = fake_open() - # Now you can make assertions about how you expect 'file' to be used. + content.write.assert_has_calls(expected_file_content) @boto3_mocking.patching @@ -105,7 +105,7 @@ class CsvReportsTest(TestCase): with patch("builtins.open", fake_open): call_command("generate_current_full_report", checkpath=False) content = fake_open() - # Now you can make assertions about how you expect 'file' to be used. + content.write.assert_has_calls(expected_file_content) @boto3_mocking.patching From ff446aa56a67c91871945af968041acaf90d2c8e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:29:49 -0700 Subject: [PATCH 094/109] Add instructions for .env --- docs/developer/README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/developer/README.md b/docs/developer/README.md index 5595b627b..57985d6e2 100644 --- a/docs/developer/README.md +++ b/docs/developer/README.md @@ -334,4 +334,21 @@ To associate a S3 instance to your sandbox, follow these steps: 7. Under the dropdown on `Select Plan`, select `basic-sandbox` 8. Under `Service Instance` enter `getgov-s3` for the name -See this [resource](https://cloud.gov/docs/services/s3/) for information on associating an S3 instance with your sandbox through the CLI. \ No newline at end of file +See this [resource](https://cloud.gov/docs/services/s3/) for information on associating an S3 instance with your sandbox through the CLI. + +### Testing your S3 instance locally +To test the S3 bucket associated with your sandbox, you will need to add four additional variables to your `.env` file. These are as follows: + +``` +AWS_S3_ACCESS_KEY_ID = "{string value of `access_key_id` in getgov-s3}" +AWS_S3_SECRET_ACCESS_KEY = "{string value of `secret_access_key` in getgov-s3}" +AWS_S3_REGION = "{string value of `region` in getgov-s3}" +AWS_S3_BUCKET_NAME = "{string value of `bucket` in getgov-s3}" +``` + +You can view these variables by running the following command: +``` +cf env getgov-{app name} +``` + +Then, copy the variables under the section labled `s3`. \ No newline at end of file From ee9fc789c78852b5baf21c57c30f91dc99df0589 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 4 Dec 2023 13:46:38 -0700 Subject: [PATCH 095/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 3b9918713..2f703ae21 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,8 +2,6 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: - # pull_request will be removed when merged - pull_request: workflow_dispatch: inputs: environment: From d0c793cfbee3acf696e75a9327eef7205f0acac9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:36:02 -0700 Subject: [PATCH 096/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 2f703ae21..45ab176f8 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -11,24 +11,15 @@ on: - stable - staging - development - - ky - - es - - nl - - rh - za - - gd - - rb - - ko - - ab - - bl - - rjm - - dk schedule: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" jobs: - variables: + variables: + env: + report_environment: CF_REPORT_ENV outputs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" @@ -38,7 +29,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : "ZA"; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[env.report_environment] }}; core.setOutput('environment', environment); upload-reports: From 3424121a33e9493615ae7c131138b421a450b13c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:44:49 -0700 Subject: [PATCH 097/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 45ab176f8..22b579c48 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,6 +2,7 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} on: + pull_request: workflow_dispatch: inputs: environment: From 28b966402c08a55dc0a7e74d636ee1647e144b49 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:47:01 -0700 Subject: [PATCH 098/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 22b579c48..5070e342a 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -19,8 +19,6 @@ on: jobs: variables: - env: - report_environment: CF_REPORT_ENV outputs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" @@ -30,7 +28,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[env.report_environment] }}; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[CF_REPORT_ENV] }}; core.setOutput('environment', environment); upload-reports: From 3b10a6b023f8ceab91d3dfe6efa4ca64dc335a04 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:49:33 -0700 Subject: [PATCH 099/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 5070e342a..aaaaac5fd 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -28,7 +28,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[CF_REPORT_ENV] }}; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets["CF_REPORT_ENV"] }}; core.setOutput('environment', environment); upload-reports: From 1c52e73fe4e4fdf9eb63c50b088d9bfaeb51fcb5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:58:44 -0700 Subject: [PATCH 100/109] Test env var --- .github/workflows/daily-csv-upload.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index aaaaac5fd..0e240bb3d 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -19,6 +19,8 @@ on: jobs: variables: + env: + DEFAULT_ENV: CF_REPORT_ENV outputs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" @@ -28,7 +30,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets["CF_REPORT_ENV"] }}; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[env.DEFAULT_ENV]] }}; core.setOutput('environment', environment); upload-reports: From 99c2dc4de3789c2ef9df3700cf00c8a2569ffdc6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:02:49 -0700 Subject: [PATCH 101/109] Fix typo --- .github/workflows/daily-csv-upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 0e240bb3d..c4d954d34 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -30,7 +30,7 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[env.DEFAULT_ENV]] }}; + const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[env.DEFAULT_ENV] }}; core.setOutput('environment', environment); upload-reports: From 20005b71408bf16cb5ce85ec34484fad4fc14eda Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:11:52 -0700 Subject: [PATCH 102/109] Set default --- .github/workflows/daily-csv-upload.yaml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index c4d954d34..82bb81657 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -3,24 +3,12 @@ run-name: Upload current-full.csv and current-federal.csv for branch ${{ github. on: pull_request: - workflow_dispatch: - inputs: - environment: - type: choice - description: Which environment do you wish to load reports for? - options: - - stable - - staging - - development - - za schedule: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" jobs: variables: - env: - DEFAULT_ENV: CF_REPORT_ENV outputs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" @@ -30,7 +18,8 @@ jobs: id: var with: script: | - const environment = (github && github.event && github.event.inputs) ? github.event.inputs.environment : ${{ secrets[env.DEFAULT_ENV] }}; + const default = "CF_REPORT_ENV" + const environment = ${{ secrets[default] }}; core.setOutput('environment', environment); upload-reports: @@ -39,6 +28,7 @@ jobs: env: CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD + CF_DEFAULT_ENV: CF_REPORT_ENV steps: - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main From 546a65ccee9cf8c95fbf3ca5a8d72fe434f2c2fe Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:18:12 -0700 Subject: [PATCH 103/109] Test default --- .github/workflows/daily-csv-upload.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 82bb81657..574be9e00 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -8,7 +8,14 @@ on: - cron: "0 5 * * *" jobs: + default: + outputs: + default: CF_REPORT_ENV + variables: + needs: [default] + env: + CF_DEFAULT: $ {{ needs.default.outputs.default }} outputs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" @@ -18,9 +25,8 @@ jobs: id: var with: script: | - const default = "CF_REPORT_ENV" - const environment = ${{ secrets[default] }}; - core.setOutput('environment', environment); + const environment = ${{ secrets[env.CF_DEFAULT] }}; + core.setOutput("environment", environment); upload-reports: runs-on: ubuntu-latest From 184c17156fc3da0528f91a8e25f77a32ea6c4eca Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:19:30 -0700 Subject: [PATCH 104/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 574be9e00..c023da000 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -10,7 +10,15 @@ on: jobs: default: outputs: - default: CF_REPORT_ENV + default: ${{ steps.var.outputs.default}} + runs-on: "ubuntu-latest" + steps: + - name: Setting global variables + uses: actions/github-script@v6 + id: var + with: + script: | + core.setOutput("default", "CF_REPORT_ENV"); variables: needs: [default] From 3a4329927741d52e2749b564aec0fdc52ac84e45 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:24:35 -0700 Subject: [PATCH 105/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index c023da000..42fe35e87 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -1,5 +1,5 @@ name: Upload current-full.csv and current-federal.csv -run-name: Upload current-full.csv and current-federal.csv for branch ${{ github.head_ref }} +run-name: Upload current-full.csv and current-federal.csv on: pull_request: @@ -7,23 +7,9 @@ on: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" -jobs: - default: - outputs: - default: ${{ steps.var.outputs.default}} - runs-on: "ubuntu-latest" - steps: - - name: Setting global variables - uses: actions/github-script@v6 - id: var - with: - script: | - core.setOutput("default", "CF_REPORT_ENV"); - variables: - needs: [default] env: - CF_DEFAULT: $ {{ needs.default.outputs.default }} + CF_DEFAULT: CF_REPORT_ENV outputs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" From d3da6d40e5768e19a30b5c80da6e6fa00ae5248e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:29:15 -0700 Subject: [PATCH 106/109] Access secret --- .github/workflows/daily-csv-upload.yaml | 28 ++++++------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 42fe35e87..43ca094db 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -7,28 +7,12 @@ on: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" - variables: - env: - CF_DEFAULT: CF_REPORT_ENV - outputs: - environment: ${{ steps.var.outputs.environment}} - runs-on: "ubuntu-latest" - steps: - - name: Setting global variables - uses: actions/github-script@v6 - id: var - with: - script: | - const environment = ${{ secrets[env.CF_DEFAULT] }}; - core.setOutput("environment", environment); - upload-reports: runs-on: ubuntu-latest needs: [variables] env: - CF_USERNAME: CF_${{ needs.variables.outputs.environment }}_USERNAME - CF_PASSWORD: CF_${{ needs.variables.outputs.environment }}_PASSWORD - CF_DEFAULT_ENV: CF_REPORT_ENV + CF_USERNAME: CF_${{ secrets.CF_REPORT_ENV }}_USERNAME + CF_PASSWORD: CF_${{ secrets.CF_REPORT_ENV }}_PASSWORD steps: - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main @@ -36,8 +20,8 @@ on: cf_username: ${{ secrets[env.CF_USERNAME] }} cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov - cf_space: ${{ needs.variables.outputs.environment }} - cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_federal_report' --name federal" + cf_space: ${{ secrets.CF_REPORT_ENV }} + cf_command: "run-task getgov-${{ secrets.CF_REPORT_ENV }} --command 'python manage.py generate_current_federal_report' --name federal" - name: Generate current-full.csv uses: cloud-gov/cg-cli-tools@main @@ -45,6 +29,6 @@ on: cf_username: ${{ secrets[env.CF_USERNAME] }} cf_password: ${{ secrets[env.CF_PASSWORD] }} cf_org: cisa-dotgov - cf_space: ${{ needs.variables.outputs.environment }} - cf_command: "run-task getgov-${{ needs.variables.outputs.environment }} --command 'python manage.py generate_current_full_report' --name full" + cf_space: ${{ secrets.CF_REPORT_ENV }} + cf_command: "run-task getgov-${{ secrets.CF_REPORT_ENV }} --command 'python manage.py generate_current_full_report' --name full" From fd3b178d857624b89021882be8a91623dfc1dc39 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:30:27 -0700 Subject: [PATCH 107/109] Update daily-csv-upload.yaml --- .github/workflows/daily-csv-upload.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 43ca094db..0addb3a98 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -9,7 +9,6 @@ on: upload-reports: runs-on: ubuntu-latest - needs: [variables] env: CF_USERNAME: CF_${{ secrets.CF_REPORT_ENV }}_USERNAME CF_PASSWORD: CF_${{ secrets.CF_REPORT_ENV }}_PASSWORD From 03d4fe88fce6922c98a4c4b4b7f3e092cbbb4c77 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:32:45 -0700 Subject: [PATCH 108/109] Add back "jobs" --- .github/workflows/daily-csv-upload.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 0addb3a98..6f2dd8448 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -7,6 +7,7 @@ on: # Runs every day at 5 AM UTC. - cron: "0 5 * * *" +jobs: upload-reports: runs-on: ubuntu-latest env: From 7c94e280805d947817bc816124f1c1cd596164ab Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:36:33 -0700 Subject: [PATCH 109/109] Remove pull_request --- .github/workflows/daily-csv-upload.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 6f2dd8448..724a19457 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -2,7 +2,6 @@ name: Upload current-full.csv and current-federal.csv run-name: Upload current-full.csv and current-federal.csv on: - pull_request: schedule: # Runs every day at 5 AM UTC. - cron: "0 5 * * *"