mirror of
https://github.com/GSA/notifications-api.git
synced 2026-07-16 11:20:41 -04:00
86
.github/workflows/adr-accepted.yml
vendored
86
.github/workflows/adr-accepted.yml
vendored
@@ -1,18 +1,86 @@
|
||||
name: ADR accepted
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: ADR accepted
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
accept:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: memorialize the ADR
|
||||
uses: 18F/adr-automation/accepted@actioning
|
||||
|
||||
- name: check for tags
|
||||
if: "${{ !contains(github.event.issue.labels.*.name, 'ADR: accepted' )}}"
|
||||
shell: bash
|
||||
run: exit 0
|
||||
|
||||
- name: checkout main branch
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
label: "ADR: accepted"
|
||||
path: docs/adrs
|
||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: get ADR number
|
||||
id: next
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p docs/adrs
|
||||
LAST_ADR=$(ls docs/adrs/*.md | grep -Eo "/[0-9]+-" | sort | tail -n1 | grep -Eo "[0-9]+")
|
||||
LAST_ADR=$(echo "$LAST_ADR" | sed -E 's/^0+//')
|
||||
NEXT_ADR=$(($LAST_ADR + 1))
|
||||
NEXT_ADR=$(printf "%04i" "$NEXT_ADR")
|
||||
echo "number=$NEXT_ADR" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: get date
|
||||
id: date
|
||||
shell: bash
|
||||
run: echo "date=$(date +'%B %d, %Y')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: build filename
|
||||
id: filename
|
||||
shell: bash
|
||||
run: |
|
||||
SLUG=$(printf '%q\n' "${{ github.event.issue.title }}" | tr A-Z a-z)
|
||||
SLUG=$(printf '%q\n' "$SLUG" | iconv -c -t ascii//TRANSLIT)
|
||||
SLUG=$(printf '%q\n' "$SLUG" | sed -E 's/[^a-z0-9]+/-/g' | sed -E 's/-+/-/g' | sed -E 's/^-+|-+$//g')
|
||||
|
||||
FILENAME="docs/adrs/${{ steps.next.outputs.number }}-$SLUG.md"
|
||||
echo "slug=$SLUG" >> "$GITHUB_OUTPUT"
|
||||
echo "filename=$FILENAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: write the ADR
|
||||
uses: DamianReeves/write-file-action@v1.2
|
||||
with:
|
||||
path: ${{ steps.filename.outputs.filename }}
|
||||
write-mode: overwrite
|
||||
contents: |
|
||||
# ${{ github.event.issue.title }}
|
||||
|
||||
Status: Accepted
|
||||
Date: ${{ steps.date.outputs.date }}
|
||||
|
||||
${{ github.event.issue.body }}
|
||||
|
||||
- name: branch, commit, and open PR
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH="adr/auto/${{ steps.filename.outputs.slug }}"
|
||||
git config --global user.email "tts@gsa.gov"
|
||||
git config --global user.name "Notify ADR Automation"
|
||||
git checkout -b $BRANCH
|
||||
git add docs/adrs/*.md
|
||||
git commit -m "add ADR ${{ steps.next.outputs.number }}: ${{ github.event.issue.title }}"
|
||||
git push -f origin $BRANCH
|
||||
gh pr create \
|
||||
--title "Add ADR ${{ steps.next.outputs.number }} to the repo" \
|
||||
--body "This pull request was opened automatically because #${{ github.event.issue.number }} was closed after being marked as an approved ADR. It contains a markdown file capturing the ADR body at the time the issue was closed. Please verify that the markdown is correct before merging!" || true
|
||||
gh pr merge $BRANCH --auto --squash || true
|
||||
|
||||
|
||||
96
README.md
96
README.md
@@ -100,53 +100,55 @@ A direct installation of PostgreSQL will not put the `createdb` command on your
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Infrastructure overview](#infrastructure-overview)
|
||||
- [GitHub Repositories](#github-repositories)
|
||||
- [Terraform](#terraform)
|
||||
- [AWS](#aws)
|
||||
- [New Relic](#new-relic)
|
||||
- [Onboarding](#onboarding)
|
||||
- [Setting up the infrastructure](#setting-up-the-infrastructure)
|
||||
- [Testing](#testing)
|
||||
- [CI testing](#ci-testing)
|
||||
- [Manual testing](#manual-testing)
|
||||
- [To run a local OWASP scan](#to-run-a-local-owasp-scan)
|
||||
- [Deploying](#deploying)
|
||||
- [Egress Proxy](#egress-proxy)
|
||||
- [Sandbox environment](#sandbox-environment)
|
||||
- [Database management](#database-management)
|
||||
- [Initial state](#initial-state)
|
||||
- [Data Model Diagram](#data-model-diagram)
|
||||
- [Migrations](#migrations)
|
||||
- [Purging user data](#purging-user-data)
|
||||
- [One-off tasks](#one-off-tasks)
|
||||
- [How messages are queued and sent](#how-messages-are-queued-and-sent)
|
||||
- [Writing public APIs](#writing-public-apis)
|
||||
- [Overview](#overview)
|
||||
- [Documenting APIs](#documenting-apis)
|
||||
- [New APIs](#new-apis)
|
||||
- [API Usage](#api-usage)
|
||||
- [Connecting to the API](#connecting-to-the-api)
|
||||
- [Postman Documentation](#postman-documentation)
|
||||
- [Using OpenAPI documentation](#using-openapi-documentation)
|
||||
- [Queues and tasks](#queues-and-tasks)
|
||||
- [Priority queue](#priority-queue)
|
||||
- [Celery scheduled tasks](#celery-scheduled-tasks)
|
||||
- [US Notify](#us-notify)
|
||||
- [System Description](#system-description)
|
||||
- [Run Book](#run-book)
|
||||
- [ Alerts, Notifications, Monitoring](#-alerts-notifications-monitoring)
|
||||
- [ Restaging Apps](#-restaging-apps)
|
||||
- [ Smoke-testing the App](#-smoke-testing-the-app)
|
||||
- [ Configuration Management](#-configuration-management)
|
||||
- [ DNS Changes](#-dns-changes)
|
||||
- [Exporting test results for compliance monitoring](#exporting-test-results-for-compliance-monitoring)
|
||||
- [ Known Gotchas](#-known-gotchas)
|
||||
- [ User Account Management](#-user-account-management)
|
||||
- [ SMS Phone Number Management](#-sms-phone-number-management)
|
||||
- [Data Storage Policies \& Procedures](#data-storage-policies--procedures)
|
||||
- [Potential PII Locations](#potential-pii-locations)
|
||||
- [Data Retention Policy](#data-retention-policy)
|
||||
- [Infrastructure overview](./docs/all.md#infrastructure-overview)
|
||||
- [GitHub Repositories](./docs/all.md#github-repositories)
|
||||
- [Terraform](./docs/all.md#terraform)
|
||||
- [AWS](./docs/all.md#aws)
|
||||
- [New Relic](./docs/all.md#new-relic)
|
||||
- [Onboarding](./docs/all.md#onboarding)
|
||||
- [Setting up the infrastructure](./docs/all.md#setting-up-the-infrastructure)
|
||||
- [Using the logs](./docs/all.md#using-the-logs)
|
||||
- [Testing](./docs/all.md#testing)
|
||||
- [CI testing](./docs/all.md#ci-testing)
|
||||
- [Manual testing](./docs/all.md#manual-testing)
|
||||
- [To run a local OWASP scan](./docs/all.md#to-run-a-local-owasp-scan)
|
||||
- [Deploying](./docs/all.md#deploying)
|
||||
- [Egress Proxy](./docs/all.md#egress-proxy)
|
||||
- [Managing environment variables](./docs/all.md#managing-environment-variables)
|
||||
- [Sandbox environment](./docs/all.md#sandbox-environment)
|
||||
- [Database management](./docs/all.md#database-management)
|
||||
- [Initial state](./docs/all.md#initial-state)
|
||||
- [Data Model Diagram](./docs/all.md#data-model-diagram)
|
||||
- [Migrations](./docs/all.md#migrations)
|
||||
- [Purging user data](./docs/all.md#purging-user-data)
|
||||
- [One-off tasks](./docs/all.md#one-off-tasks)
|
||||
- [How messages are queued and sent](./docs/all.md#how-messages-are-queued-and-sent)
|
||||
- [Writing public APIs](./docs/all.md#writing-public-apis)
|
||||
- [Overview](./docs/all.md#overview)
|
||||
- [Documenting APIs](./docs/all.md#documenting-apis)
|
||||
- [New APIs](./docs/all.md#new-apis)
|
||||
- [API Usage](./docs/all.md#api-usage)
|
||||
- [Connecting to the API](./docs/all.md#connecting-to-the-api)
|
||||
- [Postman Documentation](./docs/all.md#postman-documentation)
|
||||
- [Using OpenAPI documentation](./docs/all.md#using-openapi-documentation)
|
||||
- [Queues and tasks](./docs/all.md#queues-and-tasks)
|
||||
- [Priority queue](./docs/all.md#priority-queue)
|
||||
- [Celery scheduled tasks](./docs/all.md#celery-scheduled-tasks)
|
||||
- [US Notify](./docs/all.md#us-notify)
|
||||
- [System Description](./docs/all.md#system-description)
|
||||
- [Run Book](./docs/all.md#run-book)
|
||||
- [ Alerts, Notifications, Monitoring](./docs/all.md#-alerts-notifications-monitoring)
|
||||
- [ Restaging Apps](./docs/all.md#-restaging-apps)
|
||||
- [ Smoke-testing the App](./docs/all.md#-smoke-testing-the-app)
|
||||
- [ Configuration Management](./docs/all.md#-configuration-management)
|
||||
- [ DNS Changes](./docs/all.md#-dns-changes)
|
||||
- [Exporting test results for compliance monitoring](./docs/all.md#exporting-test-results-for-compliance-monitoring)
|
||||
- [ Known Gotchas](./docs/all.md#-known-gotchas)
|
||||
- [ User Account Management](./docs/all.md#-user-account-management)
|
||||
- [ SMS Phone Number Management](./docs/all.md#-sms-phone-number-management)
|
||||
- [Data Storage Policies \& Procedures](./docs/all.md#data-storage-policies--procedures)
|
||||
- [Potential PII Locations](./docs/all.md#potential-pii-locations)
|
||||
- [Data Retention Policy](./docs/all.md#data-retention-policy)
|
||||
|
||||
## License && public domain
|
||||
|
||||
|
||||
@@ -193,6 +193,12 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i
|
||||
return
|
||||
|
||||
try:
|
||||
job_id = notification.get("job", None)
|
||||
created_by_id = None
|
||||
if job_id:
|
||||
job = dao_get_job_by_id(job_id)
|
||||
created_by_id = job.created_by_id
|
||||
|
||||
saved_notification = persist_notification(
|
||||
template_id=notification["template"],
|
||||
template_version=notification["template_version"],
|
||||
@@ -203,6 +209,7 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i
|
||||
api_key_id=None,
|
||||
key_type=KEY_TYPE_NORMAL,
|
||||
created_at=datetime.utcnow(),
|
||||
created_by_id=created_by_id,
|
||||
job_id=notification.get("job", None),
|
||||
job_row_number=notification.get("row_number", None),
|
||||
notification_id=notification_id,
|
||||
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
import time
|
||||
|
||||
from boto3 import client
|
||||
from flask import current_app
|
||||
|
||||
from app.clients import AWS_CLIENT_CONFIG, Client
|
||||
from app.cloudfoundry_config import cloud_config
|
||||
@@ -50,6 +51,7 @@ class AwsCloudwatchClient(Client):
|
||||
# Check all cloudwatch logs from the time the notification was sent (currently 5 minutes previously) until now
|
||||
now = round(time.time() * 1000)
|
||||
beginning = sent_at
|
||||
current_app.logger.info(f"TIME RANGE TO CHECK {beginning} to {now}")
|
||||
next_token = None
|
||||
all_log_events = []
|
||||
while True:
|
||||
@@ -72,13 +74,20 @@ class AwsCloudwatchClient(Client):
|
||||
all_log_events.extend(log_events)
|
||||
if len(log_events) > 0:
|
||||
# We found it
|
||||
current_app.logger.info(
|
||||
f"WE FOUND THE EVENT WE WERE LOOKING FOR? {log_events}"
|
||||
)
|
||||
break
|
||||
next_token = response.get("nextToken")
|
||||
if not next_token:
|
||||
break
|
||||
if len(all_log_events) == 0:
|
||||
print(f"WE FOUND NO LOG EVENTS OVER TIME RANGE {beginning} to {now}")
|
||||
return all_log_events
|
||||
|
||||
def check_sms(self, message_id, notification_id, created_at):
|
||||
if os.getenv("LOCALSTACK_ENDPOINT_URL"):
|
||||
current_app.logger.info("GADZOOKS WE ARE RUNNING WITH LOCALSTACK")
|
||||
region = cloud_config.sns_region
|
||||
# TODO this clumsy approach to getting the account number will be fixed as part of notify-api #258
|
||||
account_number = cloud_config.ses_domain_arn
|
||||
@@ -87,24 +96,39 @@ class AwsCloudwatchClient(Client):
|
||||
account_number = account_number[0]
|
||||
|
||||
log_group_name = f"sns/{region}/{account_number}/DirectPublishToPhoneNumber"
|
||||
current_app.logger.info(
|
||||
f"LOG GROUP NAME: {log_group_name} MESSAGE ID: {message_id}"
|
||||
)
|
||||
filter_pattern = '{$.notification.messageId="XXXXX"}'
|
||||
filter_pattern = filter_pattern.replace("XXXXX", message_id)
|
||||
all_log_events = self._get_log(filter_pattern, log_group_name, created_at)
|
||||
current_app.logger.info(f"NUMBER OF ALL LOG EVENTS {len(all_log_events)}")
|
||||
|
||||
if all_log_events and len(all_log_events) > 0:
|
||||
current_app.logger.info(
|
||||
"SHOULD RETURN SUCCESS BECAUSE WE FOUND A SUCCESS MESSAGE FOR MESSAGE ID"
|
||||
)
|
||||
event = all_log_events[0]
|
||||
message = json.loads(event["message"])
|
||||
current_app.logger.info(f"MESSAGE {message}")
|
||||
return "success", message["delivery"]["providerResponse"]
|
||||
|
||||
log_group_name = (
|
||||
f"sns/{region}/{account_number}/DirectPublishToPhoneNumber/Failure"
|
||||
)
|
||||
current_app.logger.info(f"FAILURE LOG GROUP NAME {log_group_name}")
|
||||
all_failed_events = self._get_log(filter_pattern, log_group_name, created_at)
|
||||
current_app.logger.info(
|
||||
f"NUMBER OF ALL FAILED LOG EVENTS {len(all_failed_events)}"
|
||||
)
|
||||
if all_failed_events and len(all_failed_events) > 0:
|
||||
current_app.logger.info("SHOULD RETURN FAILED BECAUSE WE FOUND A FAILURE")
|
||||
event = all_failed_events[0]
|
||||
message = json.loads(event["message"])
|
||||
current_app.logger.info(f"MESSAGE {message}")
|
||||
return "failure", message["delivery"]["providerResponse"]
|
||||
|
||||
print(f"RAISING EXCEPTION FOR MESSAGE_ID {message_id}")
|
||||
raise Exception(
|
||||
f"No event found for message_id {message_id} notification_id {notification_id}"
|
||||
)
|
||||
|
||||
46
docs/all.md
46
docs/all.md
@@ -5,12 +5,14 @@
|
||||
- [New Relic](#new-relic)
|
||||
- [Onboarding](#onboarding)
|
||||
- [Setting up the infrastructure](#setting-up-the-infrastructure)
|
||||
- [Using the logs](#using-the-logs)
|
||||
- [Testing](#testing)
|
||||
- [CI testing](#ci-testing)
|
||||
- [Manual testing](#manual-testing)
|
||||
- [To run a local OWASP scan](#to-run-a-local-owasp-scan)
|
||||
- [Deploying](#deploying)
|
||||
- [Egress Proxy](#egress-proxy)
|
||||
- [Managing environment variables](#managing-environment-variables)
|
||||
- [Sandbox environment](#sandbox-environment)
|
||||
- [Database management](#database-management)
|
||||
- [Initial state](#initial-state)
|
||||
@@ -85,13 +87,17 @@ In addition to terraform directories in the api and admin apps above:
|
||||
|
||||
## Terraform
|
||||
|
||||
We use Terraform to manage our infrastructure, providing consistent setups across the environments.
|
||||
|
||||
Our Terraform configurations manage components via cloud.gov. This means that the configurations should work out of the box if you are using a Cloud Foundry platform, but will not work for setups based on raw AWS.
|
||||
|
||||
### Development
|
||||
|
||||
There are several remote services required for local development:
|
||||
|
||||
* s3
|
||||
* ses
|
||||
* sns
|
||||
* S3
|
||||
* SES
|
||||
* SNS
|
||||
|
||||
Credentials for these services are created by running:
|
||||
|
||||
@@ -205,6 +211,20 @@ Example answers for toll-free registration form
|
||||
|
||||

|
||||
|
||||
# Using the logs
|
||||
|
||||
If you're using the `cf` CLI, you can run `cf logs notify-api-ENV` and/or `cf logs notify-admin-ENV` to stream logs in real time. Add `--recent` to get the last few logs, though logs often move pretty quickly.
|
||||
|
||||
For general log searching, [the cloud.gov Kibana instance](https://logs.fr.cloud.gov/) is powerful, though quite complex to get started. For shortcuts to errors, some team members have New Relic access.
|
||||
|
||||
The links below will open a filtered view with logs from both applications, which can then be filtered further. However, for the links to work, you need to paste them into the URL bar while *already* logged into and viewing the Kibana page. If not, you'll just be redirected to the generic dashboard.
|
||||
|
||||
Production: https://logs.fr.cloud.gov/app/discover#/view/218a6790-596d-11ee-a43a-090d426b9a38
|
||||
Demo: https://logs.fr.cloud.gov/app/discover#/view/891392a0-596e-11ee-921a-1b6b2f4d89ed
|
||||
Staging: https://logs.fr.cloud.gov/app/discover#/view/73d7c820-596e-11ee-a43a-090d426b9a38
|
||||
|
||||
Once in the view, you'll likely want to adjust the time range in the upper right of the page.
|
||||
|
||||
# Testing
|
||||
|
||||
```
|
||||
@@ -304,6 +324,26 @@ application to a select list of allowed domains.
|
||||
Update the allowed domains by updating `deploy-config/egress_proxy/notify-api-<env>.allow.acl`
|
||||
and deploying an updated version of the application throught he normal deploy process.
|
||||
|
||||
## Managing environment variables
|
||||
|
||||
For an environment variable to make its way into the cloud.gov environment, it *must* end up in the `manifest.yml` file. Based on the deployment approach described above, there are 2 ways for this to happen.
|
||||
|
||||
### Secret environment variables
|
||||
|
||||
Because secrets are pulled from GitHub, they must be passed from our action to the deploy action and then placed into `manifest.yml`. This means that they should be in a 4 places:
|
||||
|
||||
- [ ] The GitHub secrets store
|
||||
- [ ] The deploy action in the `env` section using the format `{secrets.SECRET_NAME}`
|
||||
- [ ] The deploy action in the `push_arguments` section using the format `--var SECRET_NAME="$SECRET_NAME"`
|
||||
- [ ] The manifest using the format `SECRET_NAME: ((SECRET_NAME))`
|
||||
|
||||
### Public environment variables
|
||||
|
||||
Public env vars make up the configuration in `deploy-config`. These are pulled in together by the `--vars-file` line in the deploy action. To add or update one, it should be in 2 places:
|
||||
|
||||
- [ ] The relevant YAML file in `deploy-config` using the format `var_name: value`
|
||||
- [ ] The manifest using the format `((var_name))`
|
||||
|
||||
## Sandbox environment
|
||||
|
||||
There is a sandbox space, complete with terraform and `deploy-config/sandbox.yml` file available
|
||||
|
||||
76
poetry.lock
generated
76
poetry.lock
generated
@@ -309,33 +309,33 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "23.7.0"
|
||||
version = "23.9.1"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"},
|
||||
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"},
|
||||
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"},
|
||||
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"},
|
||||
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"},
|
||||
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"},
|
||||
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"},
|
||||
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"},
|
||||
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"},
|
||||
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"},
|
||||
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"},
|
||||
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"},
|
||||
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"},
|
||||
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"},
|
||||
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"},
|
||||
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"},
|
||||
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"},
|
||||
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"},
|
||||
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"},
|
||||
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"},
|
||||
{file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
|
||||
{file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
|
||||
{file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
|
||||
{file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
|
||||
{file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
|
||||
{file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
|
||||
{file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
|
||||
{file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
|
||||
{file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
|
||||
{file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
|
||||
{file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
|
||||
{file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
|
||||
{file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
|
||||
{file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
|
||||
{file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
|
||||
{file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
|
||||
{file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
|
||||
{file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
|
||||
{file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
|
||||
{file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
|
||||
{file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
|
||||
{file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -345,7 +345,7 @@ packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
@@ -1386,20 +1386,20 @@ smmap = ">=3.0.1,<6"
|
||||
|
||||
[[package]]
|
||||
name = "gitpython"
|
||||
version = "3.1.36"
|
||||
version = "3.1.37"
|
||||
description = "GitPython is a Python library used to interact with Git repositories"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "GitPython-3.1.36-py3-none-any.whl", hash = "sha256:8d22b5cfefd17c79914226982bb7851d6ade47545b1735a9d010a2a4c26d8388"},
|
||||
{file = "GitPython-3.1.36.tar.gz", hash = "sha256:4bb0c2a6995e85064140d31a33289aa5dce80133a23d36fcd372d716c54d3ebf"},
|
||||
{file = "GitPython-3.1.37-py3-none-any.whl", hash = "sha256:5f4c4187de49616d710a77e98ddf17b4782060a1788df441846bddefbb89ab33"},
|
||||
{file = "GitPython-3.1.37.tar.gz", hash = "sha256:f9b9ddc0761c125d5780eab2d64be4873fc6817c2899cbcb34b02344bdc7bc54"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
gitdb = ">=4.0.1,<5"
|
||||
|
||||
[package.extras]
|
||||
test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-sugar", "virtualenv"]
|
||||
test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-sugar"]
|
||||
|
||||
[[package]]
|
||||
name = "govuk-bank-holidays"
|
||||
@@ -2044,16 +2044,6 @@ files = [
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
|
||||
@@ -2455,7 +2445,7 @@ smartypants = ">=2.0.1"
|
||||
type = "git"
|
||||
url = "https://github.com/GSA/notifications-utils.git"
|
||||
reference = "main"
|
||||
resolved_reference = "d3ad5b127a257f52f6dc36fe555a4b9c980c16b3"
|
||||
resolved_reference = "5eadd38702fd187fd2954a5c763314b9b2446bea"
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
@@ -3842,13 +3832,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.11"
|
||||
version = "6.0.12.12"
|
||||
description = "Typing stubs for PyYAML"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "types-PyYAML-6.0.12.11.tar.gz", hash = "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b"},
|
||||
{file = "types_PyYAML-6.0.12.11-py3-none-any.whl", hash = "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d"},
|
||||
{file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"},
|
||||
{file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4291,4 +4281,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "642319df54dfc549b650ff145855594e79d21c6191af06c85632595feef13482"
|
||||
content-hash = "15aafd504a605f6621f3017ddfba898373885f90926eb16eef363ba5b33b53a6"
|
||||
|
||||
@@ -14,7 +14,7 @@ asn1crypto = "==1.5.1"
|
||||
async-timeout = "==4.0.2"
|
||||
attrs = "==23.1.0"
|
||||
awscli = "==1.29.15"
|
||||
black = "==23.7.0"
|
||||
black = "==23.9.1"
|
||||
bcrypt = "==3.2.2"
|
||||
beautifulsoup4 = "==4.12.2"
|
||||
billiard = "==3.6.4.0"
|
||||
|
||||
Reference in New Issue
Block a user