mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-04 02:11:11 -05:00
merge from main
This commit is contained in:
87
.github/workflows/deploy-prod.yml
vendored
Normal file
87
.github/workflows/deploy-prod.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
name: Deploy to production environment
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ production ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
# deploy-prod and deploy-demo will run in parallel now.
|
||||||
|
# TODO: Research if we want to serialize them
|
||||||
|
# by moving the jobs into a single file similar to
|
||||||
|
# https://github.com/GSA/usnotify-ssb/blob/main/.github/workflows/apply.yml
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: production
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Check for changes to Terraform
|
||||||
|
id: changed-terraform-files
|
||||||
|
uses: tj-actions/changed-files@v34
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
terraform/production
|
||||||
|
terraform/shared
|
||||||
|
.github/workflows/deploy-prod.yml
|
||||||
|
- name: Terraform init
|
||||||
|
if: steps.changed-terraform-files.outputs.any_changed == 'true'
|
||||||
|
working-directory: terraform/production
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
|
||||||
|
run: terraform init
|
||||||
|
- name: Terraform apply
|
||||||
|
if: steps.changed-terraform-files.outputs.any_changed == 'true'
|
||||||
|
working-directory: terraform/production
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
|
||||||
|
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||||
|
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||||
|
run: terraform apply -auto-approve -input=false
|
||||||
|
|
||||||
|
- uses: ./.github/actions/setup-project
|
||||||
|
- name: Install application dependencies
|
||||||
|
run: make bootstrap
|
||||||
|
|
||||||
|
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
|
||||||
|
run: pipenv requirements > requirements.txt
|
||||||
|
|
||||||
|
- name: Deploy to cloud.gov
|
||||||
|
uses: 18f/cg-deploy-action@main
|
||||||
|
env:
|
||||||
|
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
|
||||||
|
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||||
|
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||||
|
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||||
|
with:
|
||||||
|
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||||
|
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||||
|
cf_org: gsa-tts-benefits-studio-prototyping
|
||||||
|
cf_space: notify-production
|
||||||
|
push_arguments: >-
|
||||||
|
--vars-file deploy-config/production.yml
|
||||||
|
--var DANGEROUS_SALT="$DANGEROUS_SALT"
|
||||||
|
--var SECRET_KEY="$SECRET_KEY"
|
||||||
|
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||||
|
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||||
|
|
||||||
|
- name: Check for changes to egress config
|
||||||
|
id: changed-egress-config
|
||||||
|
uses: tj-actions/changed-files@v34
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
deploy-config/egress_proxy/notify-api-production.*.acl
|
||||||
|
.github/actions/deploy-proxy/action.yml
|
||||||
|
.github/workflows/deploy-prod.yml
|
||||||
|
- name: Deploy egress proxy
|
||||||
|
if: steps.changed-egress-config.outputs.any_changed == 'true'
|
||||||
|
uses: ./.github/actions/deploy-proxy
|
||||||
|
with:
|
||||||
|
cf_space: notify-production
|
||||||
|
app: notify-api-production
|
||||||
36
.github/workflows/drift.yml
vendored
36
.github/workflows/drift.yml
vendored
@@ -45,22 +45,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: terraform/demo
|
path: terraform/demo
|
||||||
|
|
||||||
# check_prod_drift:
|
check_prod_drift:
|
||||||
# runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# name: Check for drift of production terraform configuration
|
name: Check for drift of production terraform configuration
|
||||||
# environment: production
|
environment: production
|
||||||
# steps:
|
steps:
|
||||||
# - name: Checkout
|
- name: Checkout
|
||||||
# uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
# with:
|
with:
|
||||||
# ref: 'production'
|
ref: 'production'
|
||||||
|
|
||||||
# - name: Check for drift
|
- name: Check for drift
|
||||||
# uses: dflook/terraform-check@v1
|
uses: dflook/terraform-check@v1
|
||||||
# env:
|
env:
|
||||||
# AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
|
||||||
# AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
|
||||||
# TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
|
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||||
# TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||||
# with:
|
with:
|
||||||
# path: terraform/production
|
path: terraform/production
|
||||||
|
|||||||
2
.github/workflows/terraform-production.yml
vendored
2
.github/workflows/terraform-production.yml
vendored
@@ -2,7 +2,7 @@ name: Run Terraform plan in production
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ production-disabled-for-now ]
|
branches: [ production ]
|
||||||
paths: [ 'terraform/**' ]
|
paths: [ 'terraform/**' ]
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -43,6 +43,10 @@ run-celery-beat: ## Run celery beat
|
|||||||
-A run_celery.notify_celery beat \
|
-A run_celery.notify_celery beat \
|
||||||
--loglevel=INFO
|
--loglevel=INFO
|
||||||
|
|
||||||
|
.PHONY: cloudgov-user-report
|
||||||
|
cloudgov-user-report:
|
||||||
|
@pipenv run python -m terraform.ops.cloudgov_user_report
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|||||||
1
Pipfile
1
Pipfile
@@ -79,6 +79,7 @@ jinja2-cli = {version = "==0.8.2", extras = ["yaml"]}
|
|||||||
pip-audit = "*"
|
pip-audit = "*"
|
||||||
bandit = "*"
|
bandit = "*"
|
||||||
honcho = "*"
|
honcho = "*"
|
||||||
|
cloudfoundry-client = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.9"
|
python_version = "3.9"
|
||||||
|
|||||||
432
Pipfile.lock
generated
432
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "cec76bad3c666e613f7b6ef7da68232b923ae8855fa9d302446ac5f9843b634f"
|
"sha256": "a88c2c97eeb749b0acba8d801aa89ae4544574aac78ee413eaaf1dd009dab0a6"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -158,11 +158,11 @@
|
|||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
|
||||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2022.12.7"
|
"version": "==2023.5.7"
|
||||||
},
|
},
|
||||||
"cffi": {
|
"cffi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -769,7 +769,7 @@
|
|||||||
"notifications-utils": {
|
"notifications-utils": {
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"git": "https://github.com/GSA/notifications-utils.git",
|
"git": "https://github.com/GSA/notifications-utils.git",
|
||||||
"ref": "8a7db23114779d71d2eb5344670d38413d188c1d"
|
"ref": "4c0c7c7767f04bbf7dda03df0d80ff1d1861baa9"
|
||||||
},
|
},
|
||||||
"numpy": {
|
"numpy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -1055,10 +1055,10 @@
|
|||||||
},
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2",
|
"sha256:77929bc7f5dab9adf3acba2d3bb7d7658f1e0c2f1cafe7eb36434e751c471119",
|
||||||
"sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893"
|
"sha256:dc87a0bdef6c8bfe1ef1e1c40be7034390c2ae02d92dcd0c7ca1729443899880"
|
||||||
],
|
],
|
||||||
"version": "==4.5.4"
|
"version": "==4.5.5"
|
||||||
},
|
},
|
||||||
"requests": {
|
"requests": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -1268,11 +1268,11 @@
|
|||||||
},
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4866679a0722de00796a74086238bb3b98d90f423f05de039abb09315487254a",
|
"sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76",
|
||||||
"sha256:a987caf1092edc7523edb139edb20c70571c4a8d5eed02e0b547b4739174d091"
|
"sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.3.3"
|
"version": "==2.3.4"
|
||||||
},
|
},
|
||||||
"wrapt": {
|
"wrapt": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -1365,6 +1365,115 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {
|
"develop": {
|
||||||
|
"aiohttp": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03543dcf98a6619254b409be2d22b51f21ec66272be4ebda7b04e6412e4b2e14",
|
||||||
|
"sha256:03baa76b730e4e15a45f81dfe29a8d910314143414e528737f8589ec60cf7391",
|
||||||
|
"sha256:0a63f03189a6fa7c900226e3ef5ba4d3bd047e18f445e69adbd65af433add5a2",
|
||||||
|
"sha256:10c8cefcff98fd9168cdd86c4da8b84baaa90bf2da2269c6161984e6737bf23e",
|
||||||
|
"sha256:147ae376f14b55f4f3c2b118b95be50a369b89b38a971e80a17c3fd623f280c9",
|
||||||
|
"sha256:176a64b24c0935869d5bbc4c96e82f89f643bcdf08ec947701b9dbb3c956b7dd",
|
||||||
|
"sha256:17b79c2963db82086229012cff93ea55196ed31f6493bb1ccd2c62f1724324e4",
|
||||||
|
"sha256:1a45865451439eb320784918617ba54b7a377e3501fb70402ab84d38c2cd891b",
|
||||||
|
"sha256:1b3ea7edd2d24538959c1c1abf97c744d879d4e541d38305f9bd7d9b10c9ec41",
|
||||||
|
"sha256:22f6eab15b6db242499a16de87939a342f5a950ad0abaf1532038e2ce7d31567",
|
||||||
|
"sha256:3032dcb1c35bc330134a5b8a5d4f68c1a87252dfc6e1262c65a7e30e62298275",
|
||||||
|
"sha256:33587f26dcee66efb2fff3c177547bd0449ab7edf1b73a7f5dea1e38609a0c54",
|
||||||
|
"sha256:34ce9f93a4a68d1272d26030655dd1b58ff727b3ed2a33d80ec433561b03d67a",
|
||||||
|
"sha256:3a80464982d41b1fbfe3154e440ba4904b71c1a53e9cd584098cd41efdb188ef",
|
||||||
|
"sha256:3b90467ebc3d9fa5b0f9b6489dfb2c304a1db7b9946fa92aa76a831b9d587e99",
|
||||||
|
"sha256:3d89efa095ca7d442a6d0cbc755f9e08190ba40069b235c9886a8763b03785da",
|
||||||
|
"sha256:3d8ef1a630519a26d6760bc695842579cb09e373c5f227a21b67dc3eb16cfea4",
|
||||||
|
"sha256:3f43255086fe25e36fd5ed8f2ee47477408a73ef00e804cb2b5cba4bf2ac7f5e",
|
||||||
|
"sha256:40653609b3bf50611356e6b6554e3a331f6879fa7116f3959b20e3528783e699",
|
||||||
|
"sha256:41a86a69bb63bb2fc3dc9ad5ea9f10f1c9c8e282b471931be0268ddd09430b04",
|
||||||
|
"sha256:493f5bc2f8307286b7799c6d899d388bbaa7dfa6c4caf4f97ef7521b9cb13719",
|
||||||
|
"sha256:4a6cadebe132e90cefa77e45f2d2f1a4b2ce5c6b1bfc1656c1ddafcfe4ba8131",
|
||||||
|
"sha256:4c745b109057e7e5f1848c689ee4fb3a016c8d4d92da52b312f8a509f83aa05e",
|
||||||
|
"sha256:4d347a172f866cd1d93126d9b239fcbe682acb39b48ee0873c73c933dd23bd0f",
|
||||||
|
"sha256:4dac314662f4e2aa5009977b652d9b8db7121b46c38f2073bfeed9f4049732cd",
|
||||||
|
"sha256:4ddaae3f3d32fc2cb4c53fab020b69a05c8ab1f02e0e59665c6f7a0d3a5be54f",
|
||||||
|
"sha256:5393fb786a9e23e4799fec788e7e735de18052f83682ce2dfcabaf1c00c2c08e",
|
||||||
|
"sha256:59f029a5f6e2d679296db7bee982bb3d20c088e52a2977e3175faf31d6fb75d1",
|
||||||
|
"sha256:5a7bdf9e57126dc345b683c3632e8ba317c31d2a41acd5800c10640387d193ed",
|
||||||
|
"sha256:5b3f2e06a512e94722886c0827bee9807c86a9f698fac6b3aee841fab49bbfb4",
|
||||||
|
"sha256:5ce45967538fb747370308d3145aa68a074bdecb4f3a300869590f725ced69c1",
|
||||||
|
"sha256:5e14f25765a578a0a634d5f0cd1e2c3f53964553a00347998dfdf96b8137f777",
|
||||||
|
"sha256:618c901dd3aad4ace71dfa0f5e82e88b46ef57e3239fc7027773cb6d4ed53531",
|
||||||
|
"sha256:652b1bff4f15f6287550b4670546a2947f2a4575b6c6dff7760eafb22eacbf0b",
|
||||||
|
"sha256:6c08e8ed6fa3d477e501ec9db169bfac8140e830aa372d77e4a43084d8dd91ab",
|
||||||
|
"sha256:6ddb2a2026c3f6a68c3998a6c47ab6795e4127315d2e35a09997da21865757f8",
|
||||||
|
"sha256:6e601588f2b502c93c30cd5a45bfc665faaf37bbe835b7cfd461753068232074",
|
||||||
|
"sha256:6e74dd54f7239fcffe07913ff8b964e28b712f09846e20de78676ce2a3dc0bfc",
|
||||||
|
"sha256:7235604476a76ef249bd64cb8274ed24ccf6995c4a8b51a237005ee7a57e8643",
|
||||||
|
"sha256:7ab43061a0c81198d88f39aaf90dae9a7744620978f7ef3e3708339b8ed2ef01",
|
||||||
|
"sha256:7c7837fe8037e96b6dd5cfcf47263c1620a9d332a87ec06a6ca4564e56bd0f36",
|
||||||
|
"sha256:80575ba9377c5171407a06d0196b2310b679dc752d02a1fcaa2bc20b235dbf24",
|
||||||
|
"sha256:80a37fe8f7c1e6ce8f2d9c411676e4bc633a8462844e38f46156d07a7d401654",
|
||||||
|
"sha256:8189c56eb0ddbb95bfadb8f60ea1b22fcfa659396ea36f6adcc521213cd7b44d",
|
||||||
|
"sha256:854f422ac44af92bfe172d8e73229c270dc09b96535e8a548f99c84f82dde241",
|
||||||
|
"sha256:880e15bb6dad90549b43f796b391cfffd7af373f4646784795e20d92606b7a51",
|
||||||
|
"sha256:8b631e26df63e52f7cce0cce6507b7a7f1bc9b0c501fcde69742130b32e8782f",
|
||||||
|
"sha256:8c29c77cc57e40f84acef9bfb904373a4e89a4e8b74e71aa8075c021ec9078c2",
|
||||||
|
"sha256:91f6d540163f90bbaef9387e65f18f73ffd7c79f5225ac3d3f61df7b0d01ad15",
|
||||||
|
"sha256:92c0cea74a2a81c4c76b62ea1cac163ecb20fb3ba3a75c909b9fa71b4ad493cf",
|
||||||
|
"sha256:9bcb89336efa095ea21b30f9e686763f2be4478f1b0a616969551982c4ee4c3b",
|
||||||
|
"sha256:a1f4689c9a1462f3df0a1f7e797791cd6b124ddbee2b570d34e7f38ade0e2c71",
|
||||||
|
"sha256:a3fec6a4cb5551721cdd70473eb009d90935b4063acc5f40905d40ecfea23e05",
|
||||||
|
"sha256:a5d794d1ae64e7753e405ba58e08fcfa73e3fad93ef9b7e31112ef3c9a0efb52",
|
||||||
|
"sha256:a86d42d7cba1cec432d47ab13b6637bee393a10f664c425ea7b305d1301ca1a3",
|
||||||
|
"sha256:adfbc22e87365a6e564c804c58fc44ff7727deea782d175c33602737b7feadb6",
|
||||||
|
"sha256:aeb29c84bb53a84b1a81c6c09d24cf33bb8432cc5c39979021cc0f98c1292a1a",
|
||||||
|
"sha256:aede4df4eeb926c8fa70de46c340a1bc2c6079e1c40ccf7b0eae1313ffd33519",
|
||||||
|
"sha256:b744c33b6f14ca26b7544e8d8aadff6b765a80ad6164fb1a430bbadd593dfb1a",
|
||||||
|
"sha256:b7a00a9ed8d6e725b55ef98b1b35c88013245f35f68b1b12c5cd4100dddac333",
|
||||||
|
"sha256:bb96fa6b56bb536c42d6a4a87dfca570ff8e52de2d63cabebfd6fb67049c34b6",
|
||||||
|
"sha256:bbcf1a76cf6f6dacf2c7f4d2ebd411438c275faa1dc0c68e46eb84eebd05dd7d",
|
||||||
|
"sha256:bca5f24726e2919de94f047739d0a4fc01372801a3672708260546aa2601bf57",
|
||||||
|
"sha256:bf2e1a9162c1e441bf805a1fd166e249d574ca04e03b34f97e2928769e91ab5c",
|
||||||
|
"sha256:c4eb3b82ca349cf6fadcdc7abcc8b3a50ab74a62e9113ab7a8ebc268aad35bb9",
|
||||||
|
"sha256:c6cc15d58053c76eacac5fa9152d7d84b8d67b3fde92709195cb984cfb3475ea",
|
||||||
|
"sha256:c6cd05ea06daca6ad6a4ca3ba7fe7dc5b5de063ff4daec6170ec0f9979f6c332",
|
||||||
|
"sha256:c844fd628851c0bc309f3c801b3a3d58ce430b2ce5b359cd918a5a76d0b20cb5",
|
||||||
|
"sha256:c9cb1565a7ad52e096a6988e2ee0397f72fe056dadf75d17fa6b5aebaea05622",
|
||||||
|
"sha256:cab9401de3ea52b4b4c6971db5fb5c999bd4260898af972bf23de1c6b5dd9d71",
|
||||||
|
"sha256:cd468460eefef601ece4428d3cf4562459157c0f6523db89365202c31b6daebb",
|
||||||
|
"sha256:d1e6a862b76f34395a985b3cd39a0d949ca80a70b6ebdea37d3ab39ceea6698a",
|
||||||
|
"sha256:d1f9282c5f2b5e241034a009779e7b2a1aa045f667ff521e7948ea9b56e0c5ff",
|
||||||
|
"sha256:d265f09a75a79a788237d7f9054f929ced2e69eb0bb79de3798c468d8a90f945",
|
||||||
|
"sha256:db3fc6120bce9f446d13b1b834ea5b15341ca9ff3f335e4a951a6ead31105480",
|
||||||
|
"sha256:dbf3a08a06b3f433013c143ebd72c15cac33d2914b8ea4bea7ac2c23578815d6",
|
||||||
|
"sha256:de04b491d0e5007ee1b63a309956eaed959a49f5bb4e84b26c8f5d49de140fa9",
|
||||||
|
"sha256:e4b09863aae0dc965c3ef36500d891a3ff495a2ea9ae9171e4519963c12ceefd",
|
||||||
|
"sha256:e595432ac259af2d4630008bf638873d69346372d38255774c0e286951e8b79f",
|
||||||
|
"sha256:e75b89ac3bd27d2d043b234aa7b734c38ba1b0e43f07787130a0ecac1e12228a",
|
||||||
|
"sha256:ea9eb976ffdd79d0e893869cfe179a8f60f152d42cb64622fca418cd9b18dc2a",
|
||||||
|
"sha256:eafb3e874816ebe2a92f5e155f17260034c8c341dad1df25672fb710627c6949",
|
||||||
|
"sha256:ee3c36df21b5714d49fc4580247947aa64bcbe2939d1b77b4c8dcb8f6c9faecc",
|
||||||
|
"sha256:f352b62b45dff37b55ddd7b9c0c8672c4dd2eb9c0f9c11d395075a84e2c40f75",
|
||||||
|
"sha256:fabb87dd8850ef0f7fe2b366d44b77d7e6fa2ea87861ab3844da99291e81e60f",
|
||||||
|
"sha256:fe11310ae1e4cd560035598c3f29d86cef39a83d244c7466f95c27ae04850f10",
|
||||||
|
"sha256:fe7ba4a51f33ab275515f66b0a236bcde4fb5561498fe8f898d4e549b2e4509f"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==3.8.4"
|
||||||
|
},
|
||||||
|
"aiosignal": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc",
|
||||||
|
"sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.3.1"
|
||||||
|
},
|
||||||
|
"async-timeout": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15",
|
||||||
|
"sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==4.0.2"
|
||||||
|
},
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
|
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
|
||||||
@@ -1410,11 +1519,11 @@
|
|||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
|
||||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2022.12.7"
|
"version": "==2023.5.7"
|
||||||
},
|
},
|
||||||
"cffi": {
|
"cffi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -1480,6 +1589,14 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.0.12"
|
"version": "==2.0.12"
|
||||||
},
|
},
|
||||||
|
"cloudfoundry-client": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1261ff57c7309406b8e8720991d861dcede23c8ee612c80f87330815623c8753",
|
||||||
|
"sha256:8293d8027e5ad5a902806603286cbab78f9639b92229fc216f798a15023c484a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.34.2"
|
||||||
|
},
|
||||||
"coverage": {
|
"coverage": {
|
||||||
"extras": [
|
"extras": [
|
||||||
"toml"
|
"toml"
|
||||||
@@ -1613,6 +1730,86 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.2.1"
|
"version": "==1.2.1"
|
||||||
},
|
},
|
||||||
|
"frozenlist": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c",
|
||||||
|
"sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f",
|
||||||
|
"sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a",
|
||||||
|
"sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784",
|
||||||
|
"sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27",
|
||||||
|
"sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d",
|
||||||
|
"sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3",
|
||||||
|
"sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678",
|
||||||
|
"sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a",
|
||||||
|
"sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483",
|
||||||
|
"sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8",
|
||||||
|
"sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf",
|
||||||
|
"sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99",
|
||||||
|
"sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c",
|
||||||
|
"sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48",
|
||||||
|
"sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5",
|
||||||
|
"sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56",
|
||||||
|
"sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e",
|
||||||
|
"sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1",
|
||||||
|
"sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401",
|
||||||
|
"sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4",
|
||||||
|
"sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e",
|
||||||
|
"sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649",
|
||||||
|
"sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a",
|
||||||
|
"sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d",
|
||||||
|
"sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0",
|
||||||
|
"sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6",
|
||||||
|
"sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d",
|
||||||
|
"sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b",
|
||||||
|
"sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6",
|
||||||
|
"sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf",
|
||||||
|
"sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef",
|
||||||
|
"sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7",
|
||||||
|
"sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842",
|
||||||
|
"sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba",
|
||||||
|
"sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420",
|
||||||
|
"sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b",
|
||||||
|
"sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d",
|
||||||
|
"sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332",
|
||||||
|
"sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936",
|
||||||
|
"sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816",
|
||||||
|
"sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91",
|
||||||
|
"sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420",
|
||||||
|
"sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448",
|
||||||
|
"sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411",
|
||||||
|
"sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4",
|
||||||
|
"sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32",
|
||||||
|
"sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b",
|
||||||
|
"sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0",
|
||||||
|
"sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530",
|
||||||
|
"sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669",
|
||||||
|
"sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7",
|
||||||
|
"sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1",
|
||||||
|
"sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5",
|
||||||
|
"sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce",
|
||||||
|
"sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4",
|
||||||
|
"sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e",
|
||||||
|
"sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2",
|
||||||
|
"sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d",
|
||||||
|
"sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9",
|
||||||
|
"sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642",
|
||||||
|
"sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0",
|
||||||
|
"sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703",
|
||||||
|
"sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb",
|
||||||
|
"sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1",
|
||||||
|
"sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13",
|
||||||
|
"sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab",
|
||||||
|
"sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38",
|
||||||
|
"sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb",
|
||||||
|
"sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb",
|
||||||
|
"sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81",
|
||||||
|
"sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8",
|
||||||
|
"sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd",
|
||||||
|
"sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.3.3"
|
||||||
|
},
|
||||||
"gitdb": {
|
"gitdb": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a",
|
"sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a",
|
||||||
@@ -1857,6 +2054,93 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.0.5"
|
"version": "==1.0.5"
|
||||||
},
|
},
|
||||||
|
"multidict": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9",
|
||||||
|
"sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8",
|
||||||
|
"sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03",
|
||||||
|
"sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710",
|
||||||
|
"sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161",
|
||||||
|
"sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664",
|
||||||
|
"sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569",
|
||||||
|
"sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067",
|
||||||
|
"sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313",
|
||||||
|
"sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706",
|
||||||
|
"sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2",
|
||||||
|
"sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636",
|
||||||
|
"sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49",
|
||||||
|
"sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93",
|
||||||
|
"sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603",
|
||||||
|
"sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0",
|
||||||
|
"sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60",
|
||||||
|
"sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4",
|
||||||
|
"sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e",
|
||||||
|
"sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1",
|
||||||
|
"sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60",
|
||||||
|
"sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951",
|
||||||
|
"sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc",
|
||||||
|
"sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe",
|
||||||
|
"sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95",
|
||||||
|
"sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d",
|
||||||
|
"sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8",
|
||||||
|
"sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed",
|
||||||
|
"sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2",
|
||||||
|
"sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775",
|
||||||
|
"sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87",
|
||||||
|
"sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c",
|
||||||
|
"sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2",
|
||||||
|
"sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98",
|
||||||
|
"sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3",
|
||||||
|
"sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe",
|
||||||
|
"sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78",
|
||||||
|
"sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660",
|
||||||
|
"sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176",
|
||||||
|
"sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e",
|
||||||
|
"sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988",
|
||||||
|
"sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c",
|
||||||
|
"sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c",
|
||||||
|
"sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0",
|
||||||
|
"sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449",
|
||||||
|
"sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f",
|
||||||
|
"sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde",
|
||||||
|
"sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5",
|
||||||
|
"sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d",
|
||||||
|
"sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac",
|
||||||
|
"sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a",
|
||||||
|
"sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9",
|
||||||
|
"sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca",
|
||||||
|
"sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11",
|
||||||
|
"sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35",
|
||||||
|
"sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063",
|
||||||
|
"sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b",
|
||||||
|
"sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982",
|
||||||
|
"sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258",
|
||||||
|
"sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1",
|
||||||
|
"sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52",
|
||||||
|
"sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480",
|
||||||
|
"sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7",
|
||||||
|
"sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461",
|
||||||
|
"sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d",
|
||||||
|
"sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc",
|
||||||
|
"sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779",
|
||||||
|
"sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a",
|
||||||
|
"sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547",
|
||||||
|
"sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0",
|
||||||
|
"sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171",
|
||||||
|
"sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf",
|
||||||
|
"sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d",
|
||||||
|
"sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==6.0.4"
|
||||||
|
},
|
||||||
|
"oauth2-client": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5381900448ff1ae762eb7c65c501002eac46bb5ca2f49477fdfeaf9e9969f284",
|
||||||
|
"sha256:7b938ba8166128a3c4c15ad23ca0c95a2468f8e8b6069d019ebc73360c15c7ca"
|
||||||
|
],
|
||||||
|
"version": "==1.4.2"
|
||||||
|
},
|
||||||
"packageurl-python": {
|
"packageurl-python": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4bad1d3ea4feb5e7a1db5ca8fb690ac9c82ab18e08d500755947b853df68817d",
|
"sha256:4bad1d3ea4feb5e7a1db5ca8fb690ac9c82ab18e08d500755947b853df68817d",
|
||||||
@@ -1921,6 +2205,32 @@
|
|||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==1.0.0"
|
"version": "==1.0.0"
|
||||||
},
|
},
|
||||||
|
"polling2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:90b7da82cf7adbb48029724d3546af93f21ab6e592ec37c8c4619aedd010e342",
|
||||||
|
"sha256:ad86d56fbd7502f0856cac2d0109d595c18fa6c7fb12c88cee5e5d16c17286c1"
|
||||||
|
],
|
||||||
|
"version": "==0.5.0"
|
||||||
|
},
|
||||||
|
"protobuf": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03eee35b60317112a72d19c54d0bff7bc58ff12fea4cd7b018232bd99758ffdf",
|
||||||
|
"sha256:2b94bd6df92d71bd1234a2ffe7ce96ddf6d10cf637a18d6b55ad0a89fbb7fc21",
|
||||||
|
"sha256:36f5370a930cb77c8ad2f4135590c672d0d2c72d4a707c7d0058dce4b4b4a598",
|
||||||
|
"sha256:5f1eba1da2a2f3f7df469fccddef3cc060b8a16cfe3cc65961ad36b4dbcf59c5",
|
||||||
|
"sha256:6c16657d6717a0c62d5d740cb354fbad1b0d8cb811669e06fc1caa0ff4799ddd",
|
||||||
|
"sha256:6fe180b56e1169d72ecc4acbd39186339aed20af5384531b8e8979b02bbee159",
|
||||||
|
"sha256:7cb5b9a05ce52c6a782bb97de52679bd3438ff2b7460eff5da348db65650f227",
|
||||||
|
"sha256:9744e934ea5855d12191040ea198eaf704ac78665d365a89d9572e3b627c2688",
|
||||||
|
"sha256:9f5a0fbfcdcc364f3986f9ed9f8bb1328fb84114fd790423ff3d7fdb0f85c2d1",
|
||||||
|
"sha256:baca40d067dddd62141a129f244703160d278648b569e90bb0e3753067644711",
|
||||||
|
"sha256:d5a35ff54e3f62e8fc7be02bb0d2fbc212bba1a5a9cc2748090690093996f07b",
|
||||||
|
"sha256:e62fb869762b4ba18666370e2f8a18f17f8ab92dd4467295c6d38be6f8fef60b",
|
||||||
|
"sha256:ebde3a023b8e11bfa6c890ef34cd6a8b47d586f26135e86c21344fe433daf2e2"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==4.23.0"
|
||||||
|
},
|
||||||
"py": {
|
"py": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
|
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
|
||||||
@@ -2189,13 +2499,21 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.5.1"
|
"version": "==0.5.1"
|
||||||
},
|
},
|
||||||
|
"websocket-client": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40",
|
||||||
|
"sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.5.1"
|
||||||
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4866679a0722de00796a74086238bb3b98d90f423f05de039abb09315487254a",
|
"sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76",
|
||||||
"sha256:a987caf1092edc7523edb139edb20c70571c4a8d5eed02e0b547b4739174d091"
|
"sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.3.3"
|
"version": "==2.3.4"
|
||||||
},
|
},
|
||||||
"xmltodict": {
|
"xmltodict": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -2204,6 +2522,86 @@
|
|||||||
],
|
],
|
||||||
"markers": "python_version >= '3.4'",
|
"markers": "python_version >= '3.4'",
|
||||||
"version": "==0.13.0"
|
"version": "==0.13.0"
|
||||||
|
},
|
||||||
|
"yarl": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571",
|
||||||
|
"sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3",
|
||||||
|
"sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3",
|
||||||
|
"sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c",
|
||||||
|
"sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7",
|
||||||
|
"sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04",
|
||||||
|
"sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191",
|
||||||
|
"sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea",
|
||||||
|
"sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4",
|
||||||
|
"sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4",
|
||||||
|
"sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095",
|
||||||
|
"sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e",
|
||||||
|
"sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74",
|
||||||
|
"sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef",
|
||||||
|
"sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33",
|
||||||
|
"sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde",
|
||||||
|
"sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45",
|
||||||
|
"sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf",
|
||||||
|
"sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b",
|
||||||
|
"sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac",
|
||||||
|
"sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0",
|
||||||
|
"sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528",
|
||||||
|
"sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716",
|
||||||
|
"sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb",
|
||||||
|
"sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18",
|
||||||
|
"sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72",
|
||||||
|
"sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6",
|
||||||
|
"sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582",
|
||||||
|
"sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5",
|
||||||
|
"sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368",
|
||||||
|
"sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc",
|
||||||
|
"sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9",
|
||||||
|
"sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be",
|
||||||
|
"sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a",
|
||||||
|
"sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80",
|
||||||
|
"sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8",
|
||||||
|
"sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6",
|
||||||
|
"sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417",
|
||||||
|
"sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574",
|
||||||
|
"sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59",
|
||||||
|
"sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608",
|
||||||
|
"sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82",
|
||||||
|
"sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1",
|
||||||
|
"sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3",
|
||||||
|
"sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d",
|
||||||
|
"sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8",
|
||||||
|
"sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc",
|
||||||
|
"sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac",
|
||||||
|
"sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8",
|
||||||
|
"sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955",
|
||||||
|
"sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0",
|
||||||
|
"sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367",
|
||||||
|
"sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb",
|
||||||
|
"sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a",
|
||||||
|
"sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623",
|
||||||
|
"sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2",
|
||||||
|
"sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6",
|
||||||
|
"sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7",
|
||||||
|
"sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4",
|
||||||
|
"sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051",
|
||||||
|
"sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938",
|
||||||
|
"sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8",
|
||||||
|
"sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9",
|
||||||
|
"sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3",
|
||||||
|
"sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5",
|
||||||
|
"sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9",
|
||||||
|
"sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333",
|
||||||
|
"sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185",
|
||||||
|
"sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3",
|
||||||
|
"sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560",
|
||||||
|
"sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b",
|
||||||
|
"sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7",
|
||||||
|
"sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78",
|
||||||
|
"sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from werkzeug.exceptions import HTTPException as WerkzeugHTTPException
|
|||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
|
|
||||||
from app.clients import NotificationProviderClients
|
from app.clients import NotificationProviderClients
|
||||||
|
from app.clients.cloudwatch.aws_cloudwatch import AwsCloudwatchClient
|
||||||
from app.clients.document_download import DocumentDownloadClient
|
from app.clients.document_download import DocumentDownloadClient
|
||||||
from app.clients.email.aws_ses import AwsSesClient
|
from app.clients.email.aws_ses import AwsSesClient
|
||||||
from app.clients.email.aws_ses_stub import AwsSesStubClient
|
from app.clients.email.aws_ses_stub import AwsSesStubClient
|
||||||
@@ -55,6 +56,7 @@ notify_celery = NotifyCelery()
|
|||||||
aws_ses_client = AwsSesClient()
|
aws_ses_client = AwsSesClient()
|
||||||
aws_ses_stub_client = AwsSesStubClient()
|
aws_ses_stub_client = AwsSesStubClient()
|
||||||
aws_sns_client = AwsSnsClient()
|
aws_sns_client = AwsSnsClient()
|
||||||
|
aws_cloudwatch_client = AwsCloudwatchClient()
|
||||||
encryption = Encryption()
|
encryption = Encryption()
|
||||||
zendesk_client = ZendeskClient()
|
zendesk_client = ZendeskClient()
|
||||||
redis_store = RedisClient()
|
redis_store = RedisClient()
|
||||||
@@ -96,6 +98,7 @@ def create_app(application):
|
|||||||
aws_ses_stub_client.init_app(
|
aws_ses_stub_client.init_app(
|
||||||
stub_url=application.config['SES_STUB_URL']
|
stub_url=application.config['SES_STUB_URL']
|
||||||
)
|
)
|
||||||
|
aws_cloudwatch_client.init_app(application)
|
||||||
# If a stub url is provided for SES, then use the stub client rather than the real SES boto client
|
# If a stub url is provided for SES, then use the stub client rather than the real SES boto client
|
||||||
email_clients = [aws_ses_stub_client] if application.config['SES_STUB_URL'] else [aws_ses_client]
|
email_clients = [aws_ses_stub_client] if application.config['SES_STUB_URL'] else [aws_ses_client]
|
||||||
notification_provider_clients.init_app(
|
notification_provider_clients.init_app(
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
from time import time
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
from app import notify_celery
|
from app import aws_cloudwatch_client, notify_celery
|
||||||
from app.clients.email import EmailClientNonRetryableException
|
from app.clients.email import EmailClientNonRetryableException
|
||||||
from app.clients.email.aws_ses import AwsSesClientThrottlingSendRateException
|
from app.clients.email.aws_ses import AwsSesClientThrottlingSendRateException
|
||||||
from app.clients.sms import SmsClientResponseException
|
from app.clients.sms import SmsClientResponseException
|
||||||
@@ -10,17 +14,51 @@ from app.dao import notifications_dao
|
|||||||
from app.dao.notifications_dao import update_notification_status_by_id
|
from app.dao.notifications_dao import update_notification_status_by_id
|
||||||
from app.delivery import send_to_providers
|
from app.delivery import send_to_providers
|
||||||
from app.exceptions import NotificationTechnicalFailureException
|
from app.exceptions import NotificationTechnicalFailureException
|
||||||
from app.models import NOTIFICATION_TECHNICAL_FAILURE
|
from app.models import (
|
||||||
|
NOTIFICATION_FAILED,
|
||||||
|
NOTIFICATION_SENT,
|
||||||
|
NOTIFICATION_TECHNICAL_FAILURE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@notify_celery.task(bind=True, name="check_sms_delivery_receipt", max_retries=48, default_retry_delay=300)
|
||||||
|
def check_sms_delivery_receipt(self, message_id, notification_id, sent_at):
|
||||||
|
"""
|
||||||
|
This is called after deliver_sms to check the status of the message. This uses the same number of
|
||||||
|
retries and the same delay period as deliver_sms. In addition, this fires five minutes after
|
||||||
|
deliver_sms initially. So the idea is that most messages will succeed and show up in the logs quickly.
|
||||||
|
Other message will resolve successfully after a retry or to. A few will fail but it will take up to
|
||||||
|
4 hours to know for sure. The call to check_sms will raise an exception if neither a success nor a
|
||||||
|
failure appears in the cloudwatch logs, so this should keep retrying until the log appears, or until
|
||||||
|
we run out of retries.
|
||||||
|
"""
|
||||||
|
status, provider_response = aws_cloudwatch_client.check_sms(message_id, notification_id, sent_at)
|
||||||
|
if status == 'success':
|
||||||
|
status = NOTIFICATION_SENT
|
||||||
|
else:
|
||||||
|
status = NOTIFICATION_FAILED
|
||||||
|
update_notification_status_by_id(notification_id, status, provider_response=provider_response)
|
||||||
|
current_app.logger.info(f"Updated notification {notification_id} with response '{provider_response}'")
|
||||||
|
|
||||||
|
|
||||||
@notify_celery.task(bind=True, name="deliver_sms", max_retries=48, default_retry_delay=300)
|
@notify_celery.task(bind=True, name="deliver_sms", max_retries=48, default_retry_delay=300)
|
||||||
def deliver_sms(self, notification_id):
|
def deliver_sms(self, notification_id):
|
||||||
try:
|
try:
|
||||||
|
# Get the time we are doing the sending, to minimize the time period we need to check over for receipt
|
||||||
|
now = round(time() * 1000)
|
||||||
current_app.logger.info("Start sending SMS for notification id: {}".format(notification_id))
|
current_app.logger.info("Start sending SMS for notification id: {}".format(notification_id))
|
||||||
notification = notifications_dao.get_notification_by_id(notification_id)
|
notification = notifications_dao.get_notification_by_id(notification_id)
|
||||||
if not notification:
|
if not notification:
|
||||||
raise NoResultFound()
|
raise NoResultFound()
|
||||||
send_to_providers.send_sms_to_provider(notification)
|
message_id = send_to_providers.send_sms_to_provider(notification)
|
||||||
|
# We have to put it in the default US/Eastern timezone. From zones west of there, the delay
|
||||||
|
# will be ignored and it will fire immediately (although this probably only affects developer testing)
|
||||||
|
my_eta = datetime.now(ZoneInfo('US/Eastern')) + timedelta(seconds=300)
|
||||||
|
check_sms_delivery_receipt.apply_async(
|
||||||
|
[message_id, notification_id, now],
|
||||||
|
eta=my_eta,
|
||||||
|
queue=QueueNames.CHECK_SMS
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, SmsClientResponseException):
|
if isinstance(e, SmsClientResponseException):
|
||||||
current_app.logger.warning(
|
current_app.logger.warning(
|
||||||
|
|||||||
0
app/clients/cloudwatch/__init__.py
Normal file
0
app/clients/cloudwatch/__init__.py
Normal file
89
app/clients/cloudwatch/aws_cloudwatch.py
Normal file
89
app/clients/cloudwatch/aws_cloudwatch.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import json
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
from boto3 import client
|
||||||
|
|
||||||
|
from app.clients import Client
|
||||||
|
from app.cloudfoundry_config import cloud_config
|
||||||
|
|
||||||
|
|
||||||
|
class AwsCloudwatchClient(Client):
|
||||||
|
"""
|
||||||
|
This client is responsible for retrieving sms delivery receipts from cloudwatch.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def init_app(self, current_app, *args, **kwargs):
|
||||||
|
self._client = client(
|
||||||
|
"logs",
|
||||||
|
region_name=cloud_config.sns_region,
|
||||||
|
aws_access_key_id=cloud_config.sns_access_key,
|
||||||
|
aws_secret_access_key=cloud_config.sns_secret_key
|
||||||
|
)
|
||||||
|
super(Client, self).__init__(*args, **kwargs)
|
||||||
|
self.current_app = current_app
|
||||||
|
self._valid_sender_regex = re.compile(r"^\+?\d{5,14}$")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return 'cloudwatch'
|
||||||
|
|
||||||
|
def _get_log(self, my_filter, log_group_name, sent_at):
|
||||||
|
|
||||||
|
# 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
|
||||||
|
next_token = None
|
||||||
|
all_log_events = []
|
||||||
|
while True:
|
||||||
|
if next_token:
|
||||||
|
response = self._client.filter_log_events(
|
||||||
|
logGroupName=log_group_name,
|
||||||
|
filterPattern=my_filter,
|
||||||
|
nextToken=next_token,
|
||||||
|
startTime=beginning,
|
||||||
|
endTime=now
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response = self._client.filter_log_events(
|
||||||
|
logGroupName=log_group_name,
|
||||||
|
filterPattern=my_filter,
|
||||||
|
startTime=beginning,
|
||||||
|
endTime=now
|
||||||
|
)
|
||||||
|
log_events = response.get('events', [])
|
||||||
|
all_log_events.extend(log_events)
|
||||||
|
if len(log_events) > 0:
|
||||||
|
# We found it
|
||||||
|
break
|
||||||
|
next_token = response.get('nextToken')
|
||||||
|
if not next_token:
|
||||||
|
break
|
||||||
|
return all_log_events
|
||||||
|
|
||||||
|
def check_sms(self, message_id, notification_id, created_at):
|
||||||
|
|
||||||
|
# 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
|
||||||
|
account_number = account_number.replace('arn:aws:ses:us-west-2:', '')
|
||||||
|
account_number = account_number.split(":")
|
||||||
|
account_number = account_number[0]
|
||||||
|
|
||||||
|
log_group_name = f'sns/us-west-2/{account_number}/DirectPublishToPhoneNumber'
|
||||||
|
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)
|
||||||
|
|
||||||
|
if all_log_events and len(all_log_events) > 0:
|
||||||
|
event = all_log_events[0]
|
||||||
|
message = json.loads(event['message'])
|
||||||
|
return "success", message['delivery']['providerResponse']
|
||||||
|
|
||||||
|
log_group_name = f'sns/us-west-2/{account_number}/DirectPublishToPhoneNumber/Failure'
|
||||||
|
all_failed_events = self._get_log(filter_pattern, log_group_name, created_at)
|
||||||
|
if all_failed_events and len(all_failed_events) > 0:
|
||||||
|
event = all_failed_events[0]
|
||||||
|
message = json.loads(event['message'])
|
||||||
|
return "fail", message['delivery']['providerResponse']
|
||||||
|
|
||||||
|
raise Exception(f'No event found for message_id {message_id} notification_id {notification_id}')
|
||||||
@@ -39,6 +39,15 @@ class CloudfoundryConfig:
|
|||||||
domain_arn = getenv('SES_DOMAIN_ARN', 'dev.notify.gov')
|
domain_arn = getenv('SES_DOMAIN_ARN', 'dev.notify.gov')
|
||||||
return domain_arn.split('/')[-1]
|
return domain_arn.split('/')[-1]
|
||||||
|
|
||||||
|
# TODO remove this after notifications-api #258
|
||||||
|
@property
|
||||||
|
def ses_domain_arn(self):
|
||||||
|
try:
|
||||||
|
domain_arn = self._ses_credentials('domain_arn')
|
||||||
|
except KeyError:
|
||||||
|
domain_arn = getenv('SES_DOMAIN_ARN', 'dev.notify.gov')
|
||||||
|
return domain_arn
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ses_region(self):
|
def ses_region(self):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class QueueNames(object):
|
|||||||
PRIORITY = 'priority-tasks'
|
PRIORITY = 'priority-tasks'
|
||||||
DATABASE = 'database-tasks'
|
DATABASE = 'database-tasks'
|
||||||
SEND_SMS = 'send-sms-tasks'
|
SEND_SMS = 'send-sms-tasks'
|
||||||
|
CHECK_SMS = 'check-sms_tasks'
|
||||||
SEND_EMAIL = 'send-email-tasks'
|
SEND_EMAIL = 'send-email-tasks'
|
||||||
RESEARCH_MODE = 'research-mode-tasks'
|
RESEARCH_MODE = 'research-mode-tasks'
|
||||||
REPORTING = 'reporting-tasks'
|
REPORTING = 'reporting-tasks'
|
||||||
@@ -33,6 +34,7 @@ class QueueNames(object):
|
|||||||
QueueNames.PERIODIC,
|
QueueNames.PERIODIC,
|
||||||
QueueNames.DATABASE,
|
QueueNames.DATABASE,
|
||||||
QueueNames.SEND_SMS,
|
QueueNames.SEND_SMS,
|
||||||
|
QueueNames.CHECK_SMS,
|
||||||
QueueNames.SEND_EMAIL,
|
QueueNames.SEND_EMAIL,
|
||||||
QueueNames.RESEARCH_MODE,
|
QueueNames.RESEARCH_MODE,
|
||||||
QueueNames.REPORTING,
|
QueueNames.REPORTING,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ def _update_notification_status(notification, status, provider_response=None):
|
|||||||
|
|
||||||
|
|
||||||
@autocommit
|
@autocommit
|
||||||
def update_notification_status_by_id(notification_id, status, sent_by=None):
|
def update_notification_status_by_id(notification_id, status, sent_by=None, provider_response=None):
|
||||||
notification = Notification.query.with_for_update().filter(Notification.id == notification_id).first()
|
notification = Notification.query.with_for_update().filter(Notification.id == notification_id).first()
|
||||||
|
|
||||||
if not notification:
|
if not notification:
|
||||||
@@ -121,6 +121,8 @@ def update_notification_status_by_id(notification_id, status, sent_by=None):
|
|||||||
and not country_records_delivery(notification.phone_prefix)
|
and not country_records_delivery(notification.phone_prefix)
|
||||||
):
|
):
|
||||||
return None
|
return None
|
||||||
|
if provider_response:
|
||||||
|
notification.provider_response = provider_response
|
||||||
if not notification.sent_by and sent_by:
|
if not notification.sent_by and sent_by:
|
||||||
notification.sent_by = sent_by
|
notification.sent_by = sent_by
|
||||||
return _update_notification_status(
|
return _update_notification_status(
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from app.serialised_models import SerialisedService, SerialisedTemplate
|
|||||||
|
|
||||||
def send_sms_to_provider(notification):
|
def send_sms_to_provider(notification):
|
||||||
service = SerialisedService.from_id(notification.service_id)
|
service = SerialisedService.from_id(notification.service_id)
|
||||||
|
message_id = None
|
||||||
if not service.active:
|
if not service.active:
|
||||||
technical_failure(notification=notification)
|
technical_failure(notification=notification)
|
||||||
return
|
return
|
||||||
@@ -79,7 +79,7 @@ def send_sms_to_provider(notification):
|
|||||||
'international': notification.international,
|
'international': notification.international,
|
||||||
}
|
}
|
||||||
db.session.close() # no commit needed as no changes to objects have been made above
|
db.session.close() # no commit needed as no changes to objects have been made above
|
||||||
provider.send_sms(**send_sms_kwargs)
|
message_id = provider.send_sms(**send_sms_kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
notification.billable_units = template.fragment_count
|
notification.billable_units = template.fragment_count
|
||||||
dao_update_notification(notification)
|
dao_update_notification(notification)
|
||||||
@@ -88,6 +88,7 @@ def send_sms_to_provider(notification):
|
|||||||
else:
|
else:
|
||||||
notification.billable_units = template.fragment_count
|
notification.billable_units = template.fragment_count
|
||||||
update_notification_to_sending(notification, provider)
|
update_notification_to_sending(notification, provider)
|
||||||
|
return message_id
|
||||||
|
|
||||||
|
|
||||||
def send_email_to_provider(notification):
|
def send_email_to_provider(notification):
|
||||||
@@ -98,7 +99,6 @@ def send_email_to_provider(notification):
|
|||||||
return
|
return
|
||||||
if notification.status == 'created':
|
if notification.status == 'created':
|
||||||
provider = provider_to_use(EMAIL_TYPE, False)
|
provider = provider_to_use(EMAIL_TYPE, False)
|
||||||
|
|
||||||
template_dict = SerialisedTemplate.from_id_and_service_id(
|
template_dict = SerialisedTemplate.from_id_and_service_id(
|
||||||
template_id=notification.template_id, service_id=service.id, version=notification.template_version
|
template_id=notification.template_id, service_id=service.id, version=notification.template_version
|
||||||
).__dict__
|
).__dict__
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ VALID_SNS_TOPICS = Config.VALID_SNS_TOPICS
|
|||||||
|
|
||||||
_signing_cert_cache = {}
|
_signing_cert_cache = {}
|
||||||
_cert_url_re = re.compile(
|
_cert_url_re = re.compile(
|
||||||
r'sns\.([a-z]{1,3}-[a-z]+-[0-9]{1,2})\.amazonaws\.com',
|
r'sns\.([a-z]{1,3}(?:-gov)?-[a-z]+-[0-9]{1,2})\.amazonaws\.com',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
email.us-gov-west-1.amazonaws.com
|
||||||
|
sns.us-gov-west-1.amazonaws.com
|
||||||
|
gov-collector.newrelic.com
|
||||||
|
egress-proxy-notify-api-production.apps.internal
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Update this file to force a re-deploy of the egress proxy even when notify-api-production.<allow|deny>.acl haven't changed
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
# Deploying
|
# Deploying
|
||||||
|
|
||||||
We deploy automatically to cloud.gov for demo and staging environments.
|
We deploy automatically to cloud.gov for production, demo, and staging environments.
|
||||||
|
|
||||||
Deployment to staging runs via the [base deployment action](../.github/workflows/deploy.yml) on GitHub, which pulls credentials from GitHub's secrets store in the staging environment.
|
Deployment to staging runs via the [base deployment action](../.github/workflows/deploy.yml) on GitHub, which pulls credentials from GitHub's secrets store in the staging environment.
|
||||||
|
|
||||||
Deployment to demo runs via the [demo deployment action](../.github/workflows/deploy-demo.yml) on GitHub, which pulls credentials from GitHub's secrets store in the demo environment.
|
Deployment to demo runs via the [demo deployment action](../.github/workflows/deploy-demo.yml) on GitHub, which pulls credentials from GitHub's secrets store in the demo environment.
|
||||||
|
|
||||||
|
Deployment to production runs via the [production deployment action](../.github/workflows/deploy-prod.yml) on GitHub, which pulls credentials from GitHub's secrets store in the production environment.
|
||||||
|
|
||||||
The [action that we use](https://github.com/18F/cg-deploy-action) deploys using [a rolling strategy](https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html), so all deployments should have zero downtime.
|
The [action that we use](https://github.com/18F/cg-deploy-action) deploys using [a rolling strategy](https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html), so all deployments should have zero downtime.
|
||||||
|
|
||||||
The API has 2 deployment environments:
|
The API has 3 deployment environments:
|
||||||
|
|
||||||
- Staging, which deploys from `main`
|
- Staging, which deploys from `main`
|
||||||
- Demo, which deploys from `production`
|
- Demo, which deploys from `production`
|
||||||
|
- Production, which deploys from `production`
|
||||||
In the future, we will add a Production deploy environment, which will deploy in parallel to Demo.
|
|
||||||
|
|
||||||
Configurations for these are located in [the `deploy-config` folder](../deploy-config/).
|
Configurations for these are located in [the `deploy-config` folder](../deploy-config/).
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,24 @@ We are using [New Relic](https://one.newrelic.com/nr1-core?account=3389907) for
|
|||||||
|
|
||||||
These steps are required for new cloud.gov environments. Local development borrows SES & SNS infrastructure from the `notify-staging` cloud.gov space, so these steps are not required for new developers.
|
These steps are required for new cloud.gov environments. Local development borrows SES & SNS infrastructure from the `notify-staging` cloud.gov space, so these steps are not required for new developers.
|
||||||
|
|
||||||
|
### Steps to do a clean prod deploy to cloud.gov
|
||||||
|
|
||||||
|
Steps for deploying production from scratch. These can be updated for a new cloud.gov environment by subbing out `prod` or `production` for your desired environment within the steps.
|
||||||
|
|
||||||
|
1. Deploy API app
|
||||||
|
1. Update `terraform-production.yml` and `deploy-prod.yml` to point to the correct space and git branch.
|
||||||
|
1. Ensure that the `domain` module is commented out in `terraform/production/main.tf`
|
||||||
|
1. Run CI/CD pipeline on the `production` branch by opening a PR from `main` to `production`
|
||||||
|
1. Create any necessary DNS records (check `notify-api-ses-production` service credentials for instructions) within https://github.com/18f/dns
|
||||||
|
1. Follow the `Steps to prepare SES` below
|
||||||
|
1. (Optional) if using a public API route, uncomment the `domain` module and re-trigger a deploy
|
||||||
|
1. Deploy Admin app
|
||||||
|
1. Update `terraform-production.yml` and `deploy-prod.yml` to point to the correct space and git branch.
|
||||||
|
1. Ensure that the `api_network_route` and `domain` modules are commented out in `terraform/production/main.tf`
|
||||||
|
1. Run CI/CD pipeline on the `production` branch by opening a PR from `main` to `production`
|
||||||
|
1. Uncomment the `api_network_route` and `domain` modules and re-trigger a deploy
|
||||||
|
1. Create DNS records for `domain` module within https://github.com/18f/dns
|
||||||
|
|
||||||
### Steps to prepare SES
|
### Steps to prepare SES
|
||||||
|
|
||||||
1. After the first deploy of the application with the SSB-brokered SES service completes:
|
1. After the first deploy of the application with the SSB-brokered SES service completes:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ that the security of the system is maintained.
|
|||||||
|
|
||||||
Operational alerts are posted to the [#pb-notify-alerts](https://gsa-tts.slack.com/archives/C04U9BGHUDB) Slack channel. Please join this channel and enable push notifications for all messages whenever you are on call.
|
Operational alerts are posted to the [#pb-notify-alerts](https://gsa-tts.slack.com/archives/C04U9BGHUDB) Slack channel. Please join this channel and enable push notifications for all messages whenever you are on call.
|
||||||
|
|
||||||
[NewRelic](https://one.newrelic.com/) is being used for monitoring the application.
|
[NewRelic](https://one.newrelic.com/) is being used for monitoring the application. [NewRelic Dashboard](https://onenr.io/08wokrnrvwx) can be filtered by environment and API, Admin, or Both.
|
||||||
|
|
||||||
[Cloud.gov Logging](https://logs.fr.cloud.gov/) is used to view and search application and platform logs.
|
[Cloud.gov Logging](https://logs.fr.cloud.gov/) is used to view and search application and platform logs.
|
||||||
|
|
||||||
@@ -153,6 +153,7 @@ Important policies:
|
|||||||
* All users must utilize `.gov` email addresses.
|
* All users must utilize `.gov` email addresses.
|
||||||
* Users who leave the team or otherwise have role changes must have their accounts updated to reflect the new roles required (or disabled) within 14 days.
|
* Users who leave the team or otherwise have role changes must have their accounts updated to reflect the new roles required (or disabled) within 14 days.
|
||||||
* SpaceDeployer credentials must be rotated within 14 days of anyone with SpaceDeveloper cloud.gov access leaving the team.
|
* SpaceDeployer credentials must be rotated within 14 days of anyone with SpaceDeveloper cloud.gov access leaving the team.
|
||||||
|
* A user report must be created annually (See AC-2(j)). `make cloudgov-user-report` can be used to create a full report of all cloud.gov users.
|
||||||
|
|
||||||
### Types of Infrastructure Users
|
### Types of Infrastructure Users
|
||||||
|
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ app_name = us-notify-api (Demo)
|
|||||||
monitor_mode = true
|
monitor_mode = true
|
||||||
|
|
||||||
[newrelic:production]
|
[newrelic:production]
|
||||||
app_name = us-notify-api
|
app_name = us-notify-api (Production)
|
||||||
monitor_mode = true
|
monitor_mode = true
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
84
terraform/ops/cloudgov_user_report.py
Normal file
84
terraform/ops/cloudgov_user_report.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
from subprocess import check_output
|
||||||
|
|
||||||
|
from cloudfoundry_client.client import CloudFoundryClient
|
||||||
|
|
||||||
|
ORG_NAME = "gsa-tts-benefits-studio-prototyping"
|
||||||
|
|
||||||
|
|
||||||
|
client = CloudFoundryClient.build_from_cf_config()
|
||||||
|
org_guid = check_output(f"cf org {ORG_NAME} --guid", shell=True).decode().strip()
|
||||||
|
space_guids = list(map(lambda item: item['guid'], client.v3.spaces.list(organization_guids=org_guid)))
|
||||||
|
|
||||||
|
|
||||||
|
class RoleCollector:
|
||||||
|
def __init__(self):
|
||||||
|
self._map = {}
|
||||||
|
|
||||||
|
def add(self, role):
|
||||||
|
user = role.user
|
||||||
|
if self._map.get(user.guid) is None:
|
||||||
|
self._map[user.guid] = {
|
||||||
|
"user": user,
|
||||||
|
"roles": [role]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
self._map[user.guid]["roles"].append(role)
|
||||||
|
|
||||||
|
def print(self):
|
||||||
|
for user_roles in self._map.values():
|
||||||
|
user = user_roles['user']
|
||||||
|
print(f"{user.type}: {user.username} has roles:")
|
||||||
|
for role in user_roles['roles']:
|
||||||
|
if role.space:
|
||||||
|
print(f" {role.type} in {role.space.name}")
|
||||||
|
else:
|
||||||
|
print(f" {role.type}")
|
||||||
|
|
||||||
|
|
||||||
|
role_collector = RoleCollector()
|
||||||
|
|
||||||
|
|
||||||
|
class User:
|
||||||
|
def __init__(self, entity):
|
||||||
|
self.guid = entity['guid']
|
||||||
|
self._username = entity['username']
|
||||||
|
self._is_service_account = entity['origin'] != 'gsa.gov'
|
||||||
|
self.type = 'Bot' if self._is_service_account else 'User'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def username(self):
|
||||||
|
if self._is_service_account:
|
||||||
|
return client.v3.service_credential_bindings.get(
|
||||||
|
self._username, include="service_instance"
|
||||||
|
).service_instance()['name']
|
||||||
|
else:
|
||||||
|
return self._username
|
||||||
|
|
||||||
|
|
||||||
|
class Space:
|
||||||
|
def __init__(self, entity):
|
||||||
|
self.name = entity['name']
|
||||||
|
|
||||||
|
|
||||||
|
class Role:
|
||||||
|
def __init__(self, entity):
|
||||||
|
self._fields = entity
|
||||||
|
self.type = entity['type']
|
||||||
|
self.user = User(entity.user())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def space(self):
|
||||||
|
try:
|
||||||
|
return Space(self._fields.space())
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
for role in map(Role, client.v3.roles.list(organization_guids=org_guid, include="user")):
|
||||||
|
role_collector.add(role)
|
||||||
|
for role in map(Role, client.v3.roles.list(space_guids=space_guids, include="user")):
|
||||||
|
role_collector.add(role)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
role_collector.print()
|
||||||
@@ -13,7 +13,7 @@ module "database" {
|
|||||||
cf_space_name = local.cf_space_name
|
cf_space_name = local.cf_space_name
|
||||||
name = "${local.app_name}-rds-${local.env}"
|
name = "${local.app_name}-rds-${local.env}"
|
||||||
recursive_delete = local.recursive_delete
|
recursive_delete = local.recursive_delete
|
||||||
rds_plan_name = "TKTK-production-rds-plan"
|
rds_plan_name = "small-psql-redundant"
|
||||||
}
|
}
|
||||||
|
|
||||||
module "redis" {
|
module "redis" {
|
||||||
@@ -23,7 +23,7 @@ module "redis" {
|
|||||||
cf_space_name = local.cf_space_name
|
cf_space_name = local.cf_space_name
|
||||||
name = "${local.app_name}-redis-${local.env}"
|
name = "${local.app_name}-redis-${local.env}"
|
||||||
recursive_delete = local.recursive_delete
|
recursive_delete = local.recursive_delete
|
||||||
redis_plan_name = "TKTK-production-redis-plan"
|
redis_plan_name = "redis-3node-large"
|
||||||
}
|
}
|
||||||
|
|
||||||
module "csv_upload_bucket" {
|
module "csv_upload_bucket" {
|
||||||
@@ -72,9 +72,10 @@ module "sns_sms" {
|
|||||||
###########################################################################
|
###########################################################################
|
||||||
# The following lines need to be commented out for the initial `terraform apply`
|
# The following lines need to be commented out for the initial `terraform apply`
|
||||||
# It can be re-enabled after:
|
# It can be re-enabled after:
|
||||||
|
# TODO: decide on public API domain name
|
||||||
# 1) the app has first been deployed
|
# 1) the app has first been deployed
|
||||||
# 2) the route has been manually created by an OrgManager:
|
# 2) the route has been manually created by an OrgManager:
|
||||||
# `cf create-domain TKTK-org-name TKTK-production-domain-name`
|
# `cf create-domain gsa-tts-benefits-studio-prototyping api.notify.gov`
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# module "domain" {
|
# module "domain" {
|
||||||
# source = "github.com/18f/terraform-cloudgov//domain?ref=v0.2.0"
|
# source = "github.com/18f/terraform-cloudgov//domain?ref=v0.2.0"
|
||||||
@@ -85,5 +86,5 @@ module "sns_sms" {
|
|||||||
# name = "${local.app_name}-domain-${local.env}"
|
# name = "${local.app_name}-domain-${local.env}"
|
||||||
# recursive_delete = local.recursive_delete
|
# recursive_delete = local.recursive_delete
|
||||||
# cdn_plan_name = "domain"
|
# cdn_plan_name = "domain"
|
||||||
# domain_name = "TKTK-production-domain-name"
|
# domain_name = "api.notify.gov"
|
||||||
# }
|
# }
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ def test_should_call_send_sms_to_provider_from_deliver_sms_task(
|
|||||||
sample_notification,
|
sample_notification,
|
||||||
mocker):
|
mocker):
|
||||||
mocker.patch('app.delivery.send_to_providers.send_sms_to_provider')
|
mocker.patch('app.delivery.send_to_providers.send_sms_to_provider')
|
||||||
|
mocker.patch('app.celery.provider_tasks.check_sms_delivery_receipt')
|
||||||
|
|
||||||
deliver_sms(sample_notification.id)
|
deliver_sms(sample_notification.id)
|
||||||
app.delivery.send_to_providers.send_sms_to_provider.assert_called_with(sample_notification)
|
app.delivery.send_to_providers.send_sms_to_provider.assert_called_with(sample_notification)
|
||||||
|
|||||||
87
tests/app/clients/test_aws_cloudwatch.py
Normal file
87
tests/app/clients/test_aws_cloudwatch.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import pytest
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from app import aws_cloudwatch_client
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_sms_no_event_error_condition(notify_api, mocker):
|
||||||
|
boto_mock = mocker.patch.object(aws_cloudwatch_client, '_client', create=True)
|
||||||
|
# TODO
|
||||||
|
# we do this to get the AWS account number, and it seems like unit tests locally have
|
||||||
|
# access to the env variables but when we push the PR they do not. Is there a better way to get it?
|
||||||
|
mocker.patch.dict('os.environ', {"SES_DOMAIN_ARN": "1111:"})
|
||||||
|
message_id = 'aaa'
|
||||||
|
notification_id = 'bbb'
|
||||||
|
boto_mock.filter_log_events.return_value = []
|
||||||
|
with notify_api.app_context():
|
||||||
|
aws_cloudwatch_client.init_app(current_app)
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
aws_cloudwatch_client.check_sms(message_id, notification_id)
|
||||||
|
|
||||||
|
|
||||||
|
def side_effect(filterPattern, logGroupName, startTime, endTime):
|
||||||
|
if "Failure" in logGroupName and 'fail' in filterPattern:
|
||||||
|
return {
|
||||||
|
"events":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'logStreamName': '89db9712-c6d1-49f9-be7c-4caa7ed9efb1',
|
||||||
|
'message': '{"delivery":{"destination":"+1661","providerResponse":"Invalid phone number"}}',
|
||||||
|
'eventId': '37535432778099870001723210579798865345508698025292922880'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
elif 'succeed' in filterPattern:
|
||||||
|
return {
|
||||||
|
"events":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'logStreamName': '89db9712-c6d1-49f9-be7c-4caa7ed9efb1',
|
||||||
|
'timestamp': 1683147017911,
|
||||||
|
'message': '{"delivery":{"destination":"+1661","providerResponse":"Phone accepted msg"}}',
|
||||||
|
'ingestionTime': 1683147018026,
|
||||||
|
'eventId': '37535432778099870001723210579798865345508698025292922880'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {"events": []}
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_sms_success(notify_api, mocker):
|
||||||
|
aws_cloudwatch_client.init_app(current_app)
|
||||||
|
boto_mock = mocker.patch.object(aws_cloudwatch_client, '_client', create=True)
|
||||||
|
boto_mock.filter_log_events.side_effect = side_effect
|
||||||
|
mocker.patch.dict('os.environ', {"SES_DOMAIN_ARN": "1111:"})
|
||||||
|
|
||||||
|
message_id = 'succeed'
|
||||||
|
notification_id = 'ccc'
|
||||||
|
with notify_api.app_context():
|
||||||
|
aws_cloudwatch_client.check_sms(message_id, notification_id, 1000000000000)
|
||||||
|
|
||||||
|
# We check the 'success' log group first and if we find the message_id, we are done, so there is only 1 call
|
||||||
|
assert boto_mock.filter_log_events.call_count == 1
|
||||||
|
mock_call = str(boto_mock.filter_log_events.mock_calls[0])
|
||||||
|
assert 'Failure' not in mock_call
|
||||||
|
assert 'succeed' in mock_call
|
||||||
|
assert 'notification.messageId' in mock_call
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_sms_failure(notify_api, mocker):
|
||||||
|
aws_cloudwatch_client.init_app(current_app)
|
||||||
|
boto_mock = mocker.patch.object(aws_cloudwatch_client, '_client', create=True)
|
||||||
|
boto_mock.filter_log_events.side_effect = side_effect
|
||||||
|
mocker.patch.dict('os.environ', {"SES_DOMAIN_ARN": "1111:"})
|
||||||
|
|
||||||
|
message_id = 'fail'
|
||||||
|
notification_id = 'bbb'
|
||||||
|
with notify_api.app_context():
|
||||||
|
aws_cloudwatch_client.check_sms(message_id, notification_id, 1000000000000)
|
||||||
|
|
||||||
|
# We check the 'success' log group and find nothing, so we then check the 'fail' log group -- two calls.
|
||||||
|
assert boto_mock.filter_log_events.call_count == 2
|
||||||
|
mock_call = str(boto_mock.filter_log_events.mock_calls[1])
|
||||||
|
assert 'Failure' in mock_call
|
||||||
|
assert 'fail' in mock_call
|
||||||
|
assert 'notification.messageId' in mock_call
|
||||||
@@ -4,12 +4,13 @@ from app.config import QueueNames
|
|||||||
def test_queue_names_all_queues_correct():
|
def test_queue_names_all_queues_correct():
|
||||||
# Need to ensure that all_queues() only returns queue names used in API
|
# Need to ensure that all_queues() only returns queue names used in API
|
||||||
queues = QueueNames.all_queues()
|
queues = QueueNames.all_queues()
|
||||||
assert len(queues) == 15
|
assert len(queues) == 16
|
||||||
assert set([
|
assert set([
|
||||||
QueueNames.PRIORITY,
|
QueueNames.PRIORITY,
|
||||||
QueueNames.PERIODIC,
|
QueueNames.PERIODIC,
|
||||||
QueueNames.DATABASE,
|
QueueNames.DATABASE,
|
||||||
QueueNames.SEND_SMS,
|
QueueNames.SEND_SMS,
|
||||||
|
QueueNames.CHECK_SMS,
|
||||||
QueueNames.SEND_EMAIL,
|
QueueNames.SEND_EMAIL,
|
||||||
QueueNames.RESEARCH_MODE,
|
QueueNames.RESEARCH_MODE,
|
||||||
QueueNames.REPORTING,
|
QueueNames.REPORTING,
|
||||||
|
|||||||
Reference in New Issue
Block a user