Merge branch 'notify-api-869' into aanand-sqlalchemy-version-update

This commit is contained in:
Aditi Anand
2024-04-24 16:11:03 -04:00
298 changed files with 1544 additions and 1152 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
run: exit 0 run: exit 0
- name: checkout main branch - name: checkout main branch
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
ref: main ref: main
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
+6 -6
View File
@@ -36,7 +36,7 @@ jobs:
- 5432:5432 - 5432:5432
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Install application dependencies - name: Install application dependencies
run: make bootstrap run: make bootstrap
@@ -68,7 +68,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: staging environment: staging
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Install poetry packages - name: Install poetry packages
run: poetry install run: poetry install
@@ -82,18 +82,18 @@ jobs:
pip-audit: pip-audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Create requirements.txt - name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt run: poetry export --without-hashes --format=requirements.txt > requirements.txt
- uses: pypa/gh-action-pip-audit@v1.0.6 - uses: pypa/gh-action-pip-audit@v1.0.8
with: with:
inputs: requirements.txt inputs: requirements.txt
static-scan: static-scan:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Install bandit - name: Install bandit
run: pip install bandit run: pip install bandit
@@ -118,7 +118,7 @@ jobs:
# Maps tcp port 5432 on service container to the host # Maps tcp port 5432 on service container to the host
- 5432:5432 - 5432:5432
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Install application dependencies - name: Install application dependencies
run: make bootstrap run: make bootstrap
+3 -3
View File
@@ -23,7 +23,7 @@ jobs:
pip-audit: pip-audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Create requirements.txt - name: Create requirements.txt
run: poetry export --without-hashes --format=requirements.txt > requirements.txt run: poetry export --without-hashes --format=requirements.txt > requirements.txt
@@ -39,7 +39,7 @@ jobs:
static-scan: static-scan:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Install bandit - name: Install bandit
run: pip install bandit run: pip install bandit
@@ -69,7 +69,7 @@ jobs:
# Maps tcp port 5432 on service container to the host # Maps tcp port 5432 on service container to the host
- 5432:5432 - 5432:5432
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: ./.github/actions/setup-project - uses: ./.github/actions/setup-project
- name: Install application dependencies - name: Install application dependencies
run: make bootstrap run: make bootstrap
+12 -2
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: demo environment: demo
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 2 fetch-depth: 2
@@ -57,7 +57,7 @@ jobs:
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }} NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }} NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
LOGIN_DOT_GOV_REGISTRATION_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/set-up-your-profile&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" LOGIN_DOT_GOV_REGISTRATION_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/set-up-your-profile&response_type=code&scope=openid+email&state=STATE"
with: with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
@@ -74,6 +74,16 @@ jobs:
--var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD" --var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD"
--var LOGIN_DOT_GOV_REGISTRATION_URL="$LOGIN_DOT_GOV_REGISTRATION_URL" --var LOGIN_DOT_GOV_REGISTRATION_URL="$LOGIN_DOT_GOV_REGISTRATION_URL"
- name: Check for changes to templates.json
id: changed-templates
uses: tj-actions/changed-files@v41
with:
files: |
app/config_files/templates.json
- name: Update templates
if: steps.changed-templates.outputs.any_changed == 'true'
run: cf run-task notify-api-demo --command "flask command update-templates"
- name: Check for changes to egress config - name: Check for changes to egress config
id: changed-egress-config id: changed-egress-config
uses: tj-actions/changed-files@v41 uses: tj-actions/changed-files@v41
+12 -2
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: production environment: production
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 2 fetch-depth: 2
@@ -61,7 +61,7 @@ jobs:
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }} NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }} NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
LOGIN_DOT_GOV_REGISTRATION_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://beta.notify.gov/set-up-your-profile&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" LOGIN_DOT_GOV_REGISTRATION_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://beta.notify.gov/set-up-your-profile&response_type=code&scope=openid+email&state=STATE"
with: with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
@@ -78,6 +78,16 @@ jobs:
--var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD" --var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD"
--var LOGIN_DOT_GOV_REGISTRATION_URL="$LOGIN_DOT_GOV_REGISTRATION_URL" --var LOGIN_DOT_GOV_REGISTRATION_URL="$LOGIN_DOT_GOV_REGISTRATION_URL"
- name: Check for changes to templates.json
id: changed-templates
uses: tj-actions/changed-files@v41
with:
files: |
app/config_files/templates.json
- name: Update templates
if: steps.changed-templates.outputs.any_changed == 'true'
run: cf run-task notify-api-production --command "flask command update-templates"
- name: Check for changes to egress config - name: Check for changes to egress config
id: changed-egress-config id: changed-egress-config
uses: tj-actions/changed-files@v41 uses: tj-actions/changed-files@v41
+12 -2
View File
@@ -17,7 +17,7 @@ jobs:
environment: staging environment: staging
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 2 fetch-depth: 2
@@ -62,7 +62,7 @@ jobs:
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }} NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }} NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
LOGIN_DOT_GOV_REGISTRATION_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/set-up-your-profile&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" LOGIN_DOT_GOV_REGISTRATION_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/set-up-your-profile&response_type=code&scope=openid+email&state=STATE"
with: with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
@@ -79,6 +79,16 @@ jobs:
--var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD" --var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD"
--var LOGIN_DOT_GOV_REGISTRATION_URL="$LOGIN_DOT_GOV_REGISTRATION_URL" --var LOGIN_DOT_GOV_REGISTRATION_URL="$LOGIN_DOT_GOV_REGISTRATION_URL"
- name: Check for changes to templates.json
id: changed-templates
uses: tj-actions/changed-files@v41
with:
files: |
app/config_files/templates.json
- name: Update templates
if: steps.changed-templates.outputs.any_changed == 'true'
run: cf run-task notify-api-staging --command "flask command update-templates"
- name: Check for changes to egress config - name: Check for changes to egress config
id: changed-egress-config id: changed-egress-config
uses: tj-actions/changed-files@v41 uses: tj-actions/changed-files@v41
+3 -3
View File
@@ -13,7 +13,7 @@ jobs:
environment: staging environment: staging
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Check for drift - name: Check for drift
uses: dflook/terraform-check@v1 uses: dflook/terraform-check@v1
@@ -31,7 +31,7 @@ jobs:
environment: demo environment: demo
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
ref: 'production' ref: 'production'
@@ -51,7 +51,7 @@ jobs:
environment: production environment: production
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
ref: 'production' ref: 'production'
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
environment: demo environment: demo
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Terraform format - name: Terraform format
id: format id: format
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
environment: production environment: production
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Terraform format - name: Terraform format
id: format id: format
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
environment: staging environment: staging
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Terraform format - name: Terraform format
id: format id: format
+2
View File
@@ -14,9 +14,11 @@ bootstrap: ## Set up everything to run the app
make generate-version-file make generate-version-file
poetry self update poetry self update
poetry self add poetry-dotenv-plugin poetry self add poetry-dotenv-plugin
poetry lock --no-update
poetry install --sync --no-root poetry install --sync --no-root
poetry run pre-commit install poetry run pre-commit install
createdb notification_api || true createdb notification_api || true
createdb test_notification_api || true
(poetry run flask db upgrade) || true (poetry run flask db upgrade) || true
.PHONY: bootstrap-with-docker .PHONY: bootstrap-with-docker
+3 -3
View File
@@ -359,9 +359,9 @@ def setup_sqlalchemy_events(app):
connection_record.info["request_data"] = { connection_record.info["request_data"] = {
"method": request.method, "method": request.method,
"host": request.host, "host": request.host,
"url_rule": request.url_rule.rule "url_rule": (
if request.url_rule request.url_rule.rule if request.url_rule else "No endpoint"
else "No endpoint", ),
} }
# celery apps # celery apps
elif current_task: elif current_task:
+6 -6
View File
@@ -214,12 +214,12 @@ def handle_complaint(ses_message):
complaint = Complaint( complaint = Complaint(
notification_id=notification.id, notification_id=notification.id,
service_id=notification.service_id, service_id=notification.service_id,
ses_feedback_id=ses_complaint.get("feedbackId", None) ses_feedback_id=(
if ses_complaint ses_complaint.get("feedbackId", None) if ses_complaint else None
else None, ),
complaint_type=ses_complaint.get("complaintFeedbackType", None) complaint_type=(
if ses_complaint ses_complaint.get("complaintFeedbackType", None) if ses_complaint else None
else None, ),
complaint_date=ses_complaint.get("timestamp", None) if ses_complaint else None, complaint_date=ses_complaint.get("timestamp", None) if ses_complaint else None,
) )
save_complaint(complaint) save_complaint(complaint)
+2 -83
View File
@@ -1,4 +1,3 @@
import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
from flask import current_app from flask import current_app
@@ -6,7 +5,7 @@ from notifications_utils.clients.zendesk.zendesk_client import NotifySupportTick
from sqlalchemy import between from sqlalchemy import between
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from app import notify_celery, redis_store, zendesk_client from app import notify_celery, zendesk_client
from app.celery.tasks import ( from app.celery.tasks import (
get_recipient_csv_and_template_and_sender_id, get_recipient_csv_and_template_and_sender_id,
process_incomplete_jobs, process_incomplete_jobs,
@@ -24,16 +23,12 @@ from app.dao.jobs_dao import (
find_jobs_with_missing_rows, find_jobs_with_missing_rows,
find_missing_row_for_job, find_missing_row_for_job,
) )
from app.dao.notifications_dao import ( from app.dao.notifications_dao import notifications_not_yet_sent
dao_get_failed_notification_count,
notifications_not_yet_sent,
)
from app.dao.services_dao import ( from app.dao.services_dao import (
dao_find_services_sending_to_tv_numbers, dao_find_services_sending_to_tv_numbers,
dao_find_services_with_high_failure_rates, dao_find_services_with_high_failure_rates,
) )
from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago
from app.delivery.send_to_providers import provider_to_use
from app.enums import JobStatus, NotificationType from app.enums import JobStatus, NotificationType
from app.models import Job from app.models import Job
from app.notifications.process_notifications import send_notification_to_queue from app.notifications.process_notifications import send_notification_to_queue
@@ -92,82 +87,6 @@ def expire_or_delete_invitations():
raise raise
# TODO THIS IS ACTUALLY DEPRECATED, WE ARE REMOVING PHONE NUMBERS FROM THE DB
# SO THERE WILL BE NO REASON TO KEEP TRACK OF THIS COUNT
@notify_celery.task(name="check-db-notification-fails")
def check_db_notification_fails():
"""
We are going to use redis to keep track of the previous fail count.
If the number of fails is more than 100% of the limit, we want to send an alert every time this
runs, because it is urgent to fix it.
If the number is more than 25%, 50% or 75% of the limit, we only want to send an alert
on a breach. I.e., if the last number was at 23% and the current number is 27%, send an email.
But if the last number was 26% and the current is 27%, don't.
"""
last_value = redis_store.get("LAST_DB_NOTIFICATION_COUNT")
if not last_value:
last_value = 0
else:
last_value = int(last_value.decode("utf-8"))
failed_count = dao_get_failed_notification_count()
if failed_count > last_value:
redis_store.set("LAST_DB_NOTIFICATION_COUNT", failed_count)
message = ""
curr_env = os.getenv("ENVIRONMENT")
if failed_count >= MAX_NOTIFICATION_FAILS:
message = f"We are over 100% in the db for failed notifications on {curr_env}"
elif (
failed_count >= MAX_NOTIFICATION_FAILS * 0.9
and last_value < MAX_NOTIFICATION_FAILS * 0.9
):
message = (
"tts-notify-alerts@gsa.gov",
f"We crossed above 90% in the db for failed notifications on {curr_env}",
)
elif (
failed_count >= MAX_NOTIFICATION_FAILS * 0.75
and last_value < MAX_NOTIFICATION_FAILS * 0.75
):
message = (
"tts-notify-alerts@gsa.gov",
f"We crossed above 75% in the db for failed notifications on {curr_env}",
)
elif (
failed_count >= MAX_NOTIFICATION_FAILS * 0.5
and last_value < MAX_NOTIFICATION_FAILS * 0.5
):
message = (
"tts-notify-alerts@gsa.gov",
f"We crossed above 50% in the db for failed notifications on {curr_env}",
)
elif (
failed_count >= MAX_NOTIFICATION_FAILS * 0.25
and last_value < MAX_NOTIFICATION_FAILS * 0.25
):
message = (
"tts-notify-alerts@gsa.gov",
f"We crossed above 25% in the db for failed notifications on {curr_env}",
)
# suppress any spam coming from development tier
if message and curr_env != "development":
provider = provider_to_use(NotificationType.EMAIL, False)
from_address = '"{}" <{}@{}>'.format(
"Failed Notification Count Alert",
"test_sender",
current_app.config["NOTIFY_EMAIL_DOMAIN"],
)
provider.send_email(
from_address,
"tts-notify-alerts@gsa.gov",
"DB Notification Failures Level Breached",
body=str(message),
)
@notify_celery.task(name="check-job-status") @notify_celery.task(name="check-job-status")
def check_job_status(): def check_job_status():
""" """
+10 -6
View File
@@ -120,12 +120,16 @@ def create_delivery_status_callback_data(notification, service_callback_api):
"notification_status": notification.status, "notification_status": notification.status,
"notification_provider_response": notification.provider_response, # TODO do we test for provider_response? "notification_provider_response": notification.provider_response, # TODO do we test for provider_response?
"notification_created_at": notification.created_at.strftime(DATETIME_FORMAT), "notification_created_at": notification.created_at.strftime(DATETIME_FORMAT),
"notification_updated_at": notification.updated_at.strftime(DATETIME_FORMAT) "notification_updated_at": (
if notification.updated_at notification.updated_at.strftime(DATETIME_FORMAT)
else None, if notification.updated_at
"notification_sent_at": notification.sent_at.strftime(DATETIME_FORMAT) else None
if notification.sent_at ),
else None, "notification_sent_at": (
notification.sent_at.strftime(DATETIME_FORMAT)
if notification.sent_at
else None
),
"notification_type": notification.notification_type, "notification_type": notification.notification_type,
"service_callback_api_url": service_callback_api.url, "service_callback_api_url": service_callback_api.url,
"service_callback_api_bearer_token": service_callback_api.bearer_token, "service_callback_api_bearer_token": service_callback_api.bearer_token,
+17 -3
View File
@@ -18,7 +18,7 @@ from sqlalchemy import and_, text
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
from app import db from app import db, redis_store
from app.aws import s3 from app.aws import s3
from app.celery.nightly_tasks import cleanup_unfinished_jobs from app.celery.nightly_tasks import cleanup_unfinished_jobs
from app.celery.tasks import process_row from app.celery.tasks import process_row
@@ -723,8 +723,8 @@ def validate_mobile(ctx, param, value): # noqa
@click.option("-s", "--state", default="active") @click.option("-s", "--state", default="active")
@click.option("-d", "--admin", default=False, type=bool) @click.option("-d", "--admin", default=False, type=bool)
def create_test_user(name, email, mobile_number, password, auth_type, state, admin): def create_test_user(name, email, mobile_number, password, auth_type, state, admin):
if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test", "staging"]:
current_app.logger.error("Can only be run in development") current_app.logger.error("Can only be run in development, test, staging")
return return
data = { data = {
@@ -801,6 +801,20 @@ def update_templates():
data = json.load(f) data = json.load(f)
for d in data: for d in data:
_update_template(d["id"], d["name"], d["type"], d["content"], d["subject"]) _update_template(d["id"], d["name"], d["type"], d["content"], d["subject"])
_clear_templates_from_cache()
def _clear_templates_from_cache():
# When we update-templates in the db, we need to make sure to delete them
# from redis, otherwise the old versions will stick around forever.
CACHE_KEYS = [
"service-????????-????-????-????-????????????-templates",
"service-????????-????-????-????-????????????-template-????????-????-????-????-????????????-version-*", # noqa
"service-????????-????-????-????-????????????-template-????????-????-????-????-????????????-versions", # noqa
]
num_deleted = sum(redis_store.delete_by_pattern(pattern) for pattern in CACHE_KEYS)
current_app.logger.info(f"Number of templates deleted from cache {num_deleted}")
@notify_command(name="create-new-service") @notify_command(name="create-new-service")
-12
View File
@@ -199,11 +199,6 @@ class Config(object):
"schedule": timedelta(minutes=66), "schedule": timedelta(minutes=66),
"options": {"queue": QueueNames.PERIODIC}, "options": {"queue": QueueNames.PERIODIC},
}, },
"check-db-notification-fails": {
"task": "check-db-notification-fails",
"schedule": crontab(minute="18, 48"),
"options": {"queue": QueueNames.PERIODIC},
},
"check-job-status": { "check-job-status": {
"task": "check-job-status", "task": "check-job-status",
"schedule": crontab(), "schedule": crontab(),
@@ -294,11 +289,6 @@ class Config(object):
HIGH_VOLUME_SERVICE = json.loads(getenv("HIGH_VOLUME_SERVICE", "[]")) HIGH_VOLUME_SERVICE = json.loads(getenv("HIGH_VOLUME_SERVICE", "[]"))
TEMPLATE_PREVIEW_API_HOST = getenv(
"TEMPLATE_PREVIEW_API_HOST", "http://localhost:6013"
)
TEMPLATE_PREVIEW_API_KEY = getenv("TEMPLATE_PREVIEW_API_KEY", "my-secret-key")
DOCUMENT_DOWNLOAD_API_HOST = getenv( DOCUMENT_DOWNLOAD_API_HOST = getenv(
"DOCUMENT_DOWNLOAD_API_HOST", "http://localhost:7000" "DOCUMENT_DOWNLOAD_API_HOST", "http://localhost:7000"
) )
@@ -356,8 +346,6 @@ class Test(Development):
"broker_url": "you-forgot-to-mock-celery-in-your-tests://", "broker_url": "you-forgot-to-mock-celery-in-your-tests://",
} }
TEMPLATE_PREVIEW_API_HOST = "http://localhost:9999"
class Production(Config): class Production(Config):
# buckets # buckets
+2 -1
View File
@@ -23,6 +23,7 @@
"Click this link to create an account on Notify.gov:", "Click this link to create an account on Notify.gov:",
"", "",
"[Join Service](((url)))", "[Join Service](((url)))",
"If youre new to Notify.gov you will first be directed to Login.gov create an account with us.",
"", "",
"", "",
"This invitation will stop working at midnight tomorrow. This is to keep ((service_name)) secure." "This invitation will stop working at midnight tomorrow. This is to keep ((service_name)) secure."
@@ -33,7 +34,7 @@
"name": "Notify SMS verify code", "name": "Notify SMS verify code",
"type": "sms", "type": "sms",
"subject": "", "subject": "",
"content": ["((verify_code)) is your Notify.gov authentication code"] "content": ["((verify_code)) is your Notify.gov authentication code."]
}, },
{ {
"id": "474e9242-823b-4f99-813d-ed392e7f1201", "id": "474e9242-823b-4f99-813d-ed392e7f1201",
+1
View File
@@ -14,6 +14,7 @@ Lastly when to create a version is done manually in dao_utils version decorator
session events. session events.
""" """
import datetime import datetime
from sqlalchemy import Column, ForeignKeyConstraint, Integer, Table, util from sqlalchemy import Column, ForeignKeyConstraint, Integer, Table, util
+8 -8
View File
@@ -90,6 +90,14 @@ def get_all_notifications_for_service_job(service_id, job_id):
notification.to = recipient notification.to = recipient
notification.normalised_to = recipient notification.normalised_to = recipient
for notification in paginated_notifications.items:
if notification.job_id is not None:
notification.personalisation = get_personalisation_from_s3(
notification.service_id,
notification.job_id,
notification.job_row_number,
)
notifications = None notifications = None
if data.get("format_for_csv"): if data.get("format_for_csv"):
notifications = [ notifications = [
@@ -101,14 +109,6 @@ def get_all_notifications_for_service_job(service_id, job_id):
paginated_notifications.items, many=True paginated_notifications.items, many=True
) )
for notification in paginated_notifications.items:
if notification.job_id is not None:
notification.personalisation = get_personalisation_from_s3(
notification.service_id,
notification.job_id,
notification.job_row_number,
)
return ( return (
jsonify( jsonify(
notifications=notifications, notifications=notifications,
+15 -15
View File
@@ -772,9 +772,9 @@ class ServiceSmsSender(db.Model):
"service_id": str(self.service_id), "service_id": str(self.service_id),
"is_default": self.is_default, "is_default": self.is_default,
"archived": self.archived, "archived": self.archived,
"inbound_number_id": str(self.inbound_number_id) "inbound_number_id": (
if self.inbound_number_id str(self.inbound_number_id) if self.inbound_number_id else None
else None, ),
"created_at": self.created_at.strftime(DATETIME_FORMAT), "created_at": self.created_at.strftime(DATETIME_FORMAT),
"updated_at": get_dt_string_or_none(self.updated_at), "updated_at": get_dt_string_or_none(self.updated_at),
} }
@@ -1184,9 +1184,9 @@ class TemplateBase(db.Model):
"created_by": self.created_by.email_address, "created_by": self.created_by.email_address,
"version": self.version, "version": self.version,
"body": self.content, "body": self.content,
"subject": self.subject "subject": (
if self.template_type == TemplateType.EMAIL self.subject if self.template_type == TemplateType.EMAIL else None
else None, ),
"name": self.name, "name": self.name,
"personalisation": { "personalisation": {
key: { key: {
@@ -1690,9 +1690,9 @@ class Notification(db.Model):
def serialize_for_csv(self): def serialize_for_csv(self):
serialized = { serialized = {
"row_number": "" "row_number": (
if self.job_row_number is None "" if self.job_row_number is None else self.job_row_number + 1
else self.job_row_number + 1, ),
"recipient": self.to, "recipient": self.to,
"client_reference": self.client_reference or "", "client_reference": self.client_reference or "",
"template_name": self.template.name, "template_name": self.template.name,
@@ -1718,12 +1718,12 @@ class Notification(db.Model):
serialized = { serialized = {
"id": self.id, "id": self.id,
"reference": self.client_reference, "reference": self.client_reference,
"email_address": self.to "email_address": (
if self.notification_type == NotificationType.EMAIL self.to if self.notification_type == NotificationType.EMAIL else None
else None, ),
"phone_number": self.to "phone_number": (
if self.notification_type == NotificationType.SMS self.to if self.notification_type == NotificationType.SMS else None
else None, ),
"line_1": None, "line_1": None,
"line_2": None, "line_2": None,
"line_3": None, "line_3": None,
+10 -8
View File
@@ -28,7 +28,6 @@ from app.organization.organization_schema import (
post_update_invited_org_user_status_schema, post_update_invited_org_user_status_schema,
) )
from app.schema_validation import validate from app.schema_validation import validate
from app.utils import hilite
organization_invite_blueprint = Blueprint("organization_invite", __name__) organization_invite_blueprint = Blueprint("organization_invite", __name__)
@@ -53,6 +52,15 @@ def invite_user_to_org(organization_id):
current_app.config["ORGANIZATION_INVITATION_EMAIL_TEMPLATE_ID"] current_app.config["ORGANIZATION_INVITATION_EMAIL_TEMPLATE_ID"]
) )
token = generate_token(
str(invited_org_user.email_address),
current_app.config["SECRET_KEY"],
current_app.config["DANGEROUS_SALT"],
)
url = os.environ["LOGIN_DOT_GOV_REGISTRATION_URL"]
url = url.replace("NONCE", token)
url = url.replace("STATE", token)
personalisation = { personalisation = {
"user_name": ( "user_name": (
"The Notify.gov team" "The Notify.gov team"
@@ -60,7 +68,7 @@ def invite_user_to_org(organization_id):
else invited_org_user.invited_by.name else invited_org_user.invited_by.name
), ),
"organization_name": invited_org_user.organization.name, "organization_name": invited_org_user.organization.name,
"url": os.environ["LOGIN_DOT_GOV_REGISTRATION_URL"], "url": url,
} }
saved_notification = persist_notification( saved_notification = persist_notification(
template_id=template.id, template_id=template.id,
@@ -89,12 +97,6 @@ def invite_user_to_org(organization_id):
organization_id, organization_id,
ex=3600 * 24, ex=3600 * 24,
) )
current_app.logger.info(
hilite(f"STORING THIS ORGANIZATION ID IN REDIS {redis_store.get(redis_key)}")
)
current_app.logger.info(
hilite(f"URL: {os.environ['LOGIN_DOT_GOV_REGISTRATION_URL']}")
)
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
return jsonify(data=invited_org_user.serialize()), 201 return jsonify(data=invited_org_user.serialize()), 201
+6 -6
View File
@@ -180,12 +180,12 @@ def volumes_by_service_report():
{ {
"service_name": row.service_name, "service_name": row.service_name,
"service_id": str(row.service_id), "service_id": str(row.service_id),
"organization_name": row.organization_name "organization_name": (
if row.organization_name row.organization_name if row.organization_name else ""
else "", ),
"organization_id": str(row.organization_id) "organization_id": (
if row.organization_id str(row.organization_id) if row.organization_id else ""
else "", ),
"free_allowance": int(row.free_allowance), "free_allowance": int(row.free_allowance),
"sms_notifications": int(row.sms_notifications), "sms_notifications": int(row.sms_notifications),
"sms_chargeable_units": int(row.sms_chargeable_units), "sms_chargeable_units": int(row.sms_chargeable_units),
+18 -10
View File
@@ -102,7 +102,7 @@ from app.service.service_senders_schema import (
) )
from app.service.utils import get_guest_list_objects from app.service.utils import get_guest_list_objects
from app.user.users_schema import post_set_permissions_schema from app.user.users_schema import post_set_permissions_schema
from app.utils import get_prev_next_pagination_links from app.utils import get_prev_next_pagination_links, hilite
service_blueprint = Blueprint("service", __name__) service_blueprint = Blueprint("service", __name__)
@@ -314,7 +314,9 @@ def get_users_for_service(service_id):
def add_user_to_service(service_id, user_id): def add_user_to_service(service_id, user_id):
service = dao_fetch_service_by_id(service_id) service = dao_fetch_service_by_id(service_id)
user = get_user_by_id(user_id=user_id) user = get_user_by_id(user_id=user_id)
# TODO REMOVE DEBUG
print(hilite(f"GOING TO ADD {user.name} to service {service.name}"))
# END DEBUG
if user in service.users: if user in service.users:
error = "User id: {} already part of service id: {}".format(user_id, service_id) error = "User id: {} already part of service id: {}".format(user_id, service_id)
raise InvalidRequest(error, status_code=400) raise InvalidRequest(error, status_code=400)
@@ -329,6 +331,10 @@ def add_user_to_service(service_id, user_id):
folder_permissions = data.get("folder_permissions", []) folder_permissions = data.get("folder_permissions", [])
dao_add_user_to_service(service, user, permissions, folder_permissions) dao_add_user_to_service(service, user, permissions, folder_permissions)
# TODO REMOVE DEBUG
print(hilite(f"ADDED {user.name} to service {service.name}"))
# END DEBUG
data = service_schema.dump(service) data = service_schema.dump(service)
return jsonify(data=data), 201 return jsonify(data=data), 201
@@ -479,14 +485,16 @@ def get_all_notifications_for_service(service_id):
jsonify( jsonify(
notifications=notifications, notifications=notifications,
page_size=page_size, page_size=page_size,
links=get_prev_next_pagination_links( links=(
page, get_prev_next_pagination_links(
len(next_page_of_pagination.items), page,
".get_all_notifications_for_service", len(next_page_of_pagination.items),
**kwargs, ".get_all_notifications_for_service",
) **kwargs,
if count_pages )
else {}, if count_pages
else {}
),
), ),
200, 200,
) )
+5 -3
View File
@@ -28,9 +28,11 @@ def send_notification_to_service_users(
notification = persist_notification( notification = persist_notification(
template_id=template.id, template_id=template.id,
template_version=template.version, template_version=template.version,
recipient=user.email_address recipient=(
if template.template_type == TemplateType.EMAIL user.email_address
else user.mobile_number, if template.template_type == TemplateType.EMAIL
else user.mobile_number
),
service=notify_service, service=notify_service,
personalisation=personalisation, personalisation=personalisation,
notification_type=template.template_type, notification_type=template.template_type,
+23 -6
View File
@@ -33,15 +33,29 @@ register_errors(service_invite)
def _create_service_invite(invited_user, invite_link_host): def _create_service_invite(invited_user, invite_link_host):
# TODO REMOVE DEBUG
print(hilite("ENTER _create_service_invite"))
# END DEBUG
template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"] template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"]
template = dao_get_template_by_id(template_id) template = dao_get_template_by_id(template_id)
service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"]) service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"])
token = generate_token(
str(invited_user.email_address),
current_app.config["SECRET_KEY"],
current_app.config["DANGEROUS_SALT"],
)
url = os.environ["LOGIN_DOT_GOV_REGISTRATION_URL"]
url = url.replace("NONCE", token)
url = url.replace("STATE", token)
personalisation = { personalisation = {
"user_name": invited_user.from_user.name, "user_name": invited_user.from_user.name,
"service_name": invited_user.service.name, "service_name": invited_user.service.name,
"url": os.environ["LOGIN_DOT_GOV_REGISTRATION_URL"], "url": url,
} }
saved_notification = persist_notification( saved_notification = persist_notification(
@@ -76,14 +90,17 @@ def _create_service_invite(invited_user, invite_link_host):
# This is for the login.gov service invite on the # This is for the login.gov service invite on the
# "Set Up Your Profile" path. # "Set Up Your Profile" path.
redis_store.set( redis_key = f"service-invite-{invited_user.email_address}"
f"service-invite-{invited_user.email_address}", redis_store.raw_set(
redis_key,
json.dumps(data), json.dumps(data),
ex=3600 * 24, ex=3600 * 24,
) )
current_app.logger.info( # TODO REMOVE DEBUG
hilite(f"STORING ALL THIS IN REDIS FOR SERVICE INVITE {json.dumps(data)}") print(hilite(f"Save this data {data} with this redis_key {redis_key}"))
) did_we_save_it = redis_store.raw_get(redis_key)
print(hilite(f"Did we save the data successfully? {did_we_save_it}"))
# END DEBUG
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
+5 -3
View File
@@ -59,7 +59,9 @@ def get_last_used_datetime_for_template(service_id, template_id):
) )
return jsonify( return jsonify(
last_date_used=last_date_used.strftime(DATETIME_FORMAT) last_date_used=(
if last_date_used last_date_used.strftime(DATETIME_FORMAT)
else last_date_used if last_date_used
else last_date_used
)
) )
+5 -3
View File
@@ -38,9 +38,11 @@ def get_paginated_uploads(service_id, limit_days, page):
"id": upload.id, "id": upload.id,
"original_file_name": upload.original_file_name, "original_file_name": upload.original_file_name,
"notification_count": upload.notification_count, "notification_count": upload.notification_count,
"created_at": upload.scheduled_for.strftime("%Y-%m-%d %H:%M:%S") "created_at": (
if upload.scheduled_for upload.scheduled_for.strftime("%Y-%m-%d %H:%M:%S")
else upload.created_at.strftime("%Y-%m-%d %H:%M:%S"), if upload.scheduled_for
else upload.created_at.strftime("%Y-%m-%d %H:%M:%S")
),
"upload_type": upload.upload_type, "upload_type": upload.upload_type,
"template_type": upload.template_type, "template_type": upload.template_type,
"recipient": upload.recipient, "recipient": upload.recipient,
+5 -3
View File
@@ -212,9 +212,11 @@ def save_email_or_sms_to_queue(
"id": notification_id, "id": notification_id,
"template_id": str(template.id), "template_id": str(template.id),
"template_version": template.version, "template_version": template.version,
"to": form["email_address"] "to": (
if notification_type == NotificationType.EMAIL form["email_address"]
else form["phone_number"], if notification_type == NotificationType.EMAIL
else form["phone_number"]
),
"service_id": str(service_id), "service_id": str(service_id),
"personalisation": personalisation, "personalisation": personalisation,
"notification_type": notification_type, "notification_type": notification_type,
+13
View File
@@ -254,6 +254,9 @@ We do not maintain any hooks in this repository.
# install dependencies, etc. # install dependencies, etc.
make bootstrap make bootstrap
# Create test database
createdb test_notification_api
make test make test
``` ```
@@ -528,6 +531,16 @@ cf run-task CLOUD-GOV-APP --command "flask command update-templates" --name YOUR
[Here's more documentation](https://docs.cloudfoundry.org/devguide/using-tasks.html) about Cloud Foundry tasks. [Here's more documentation](https://docs.cloudfoundry.org/devguide/using-tasks.html) about Cloud Foundry tasks.
# Commonly run commands
(Note: to obtain the CLOUD_GOV_APP name, run `cf apps` and find the name of the app for the tier you are targeting)
To promote a user to platform admin:
cf run-task <CLOUD_GOV_APP from cf apps see above> --command "flask command promote-user-to-platform-admin --user-email-address=<user email address>"
To update templates:
cf run-task <CLOUD_GOV_APP from cf apps see above> --command "flask command update-templates"
# Commands for test loading the local dev database # Commands for test loading the local dev database
All commands use the `-g` or `--generate` to determine how many instances to load to the db. The `-g` or `--generate` option is required and will always defult to 1. An example: `flask command add-test-uses-to-db -g 6` will generate 6 random users and insert them into the db. All commands use the `-g` or `--generate` to determine how many instances to load to the db. The `-g` or `--generate` option is required and will always defult to 1. An example: `flask command add-test-uses-to-db -g 6` will generate 6 random users and insert them into the db.
@@ -5,6 +5,7 @@ Revises: 0086_add_norm_to_notification
Create Date: 2017-05-15 12:50:20.041950 Create Date: 2017-05-15 12:50:20.041950
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0092_add_inbound_provider
Create Date: 2017-06-06 14:37:30.051647 Create Date: 2017-06-06 14:37:30.051647
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0097_notnull_inbound_provider
Create Date: 2017-06-13 15:02:33.609656 Create Date: 2017-06-13 15:02:33.609656
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0108_change_logo_not_nullable
Create Date: 2017-07-10 14:25:15.712055 Create Date: 2017-07-10 14:25:15.712055
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0111_drop_old_service_flags
Create Date: 2017-07-12 13:35:45.636618 Create Date: 2017-07-12 13:35:45.636618
""" """
from datetime import datetime from datetime import datetime
import sqlalchemy as sa import sqlalchemy as sa
@@ -5,6 +5,7 @@ Revises: 0113_job_created_by_nullable
Create Date: 2017-07-27 13:36:37.304344 Create Date: 2017-07-27 13:36:37.304344
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0118_service_sms_senders
Create Date: 2017-09-07 15:29:49.087143 Create Date: 2017-09-07 15:29:49.087143
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0119_add_email_reply_to
Create Date: 2017-09-18 14:18:49.087143 Create Date: 2017-09-18 14:18:49.087143
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0120_add_org_banner_branding
Create Date: 2017-09-20 11:00:20.415523 Create Date: 2017-09-20 11:00:20.415523
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0121_nullable_logos
Create Date: 2017-09-21 12:16:02.975120 Create Date: 2017-09-21 12:16:02.975120
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0122_add_service_letter_contact
Create Date: 2017-09-27 09:42:39.412731 Create Date: 2017-09-27 09:42:39.412731
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0123_add_noti_to_email_reply
Create Date: 2017-10-10 11:30:16.225980 Create Date: 2017-10-10 11:30:16.225980
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0124_add_free_sms_fragment_limit
Create Date: 2017-10-05 14:03:00.248005 Create Date: 2017-10-05 14:03:00.248005
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0125_add_organisation_type
Create Date: 2017-10-19 11:38:32.849573 Create Date: 2017-10-19 11:38:32.849573
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0126_add_annual_billing
Create Date: 2017-10-17 16:47:37.826333 Create Date: 2017-10-17 16:47:37.826333
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0127_remove_unique_constraint
Create Date: 2017-10-26 15:17:00.752706 Create Date: 2017-10-26 15:17:00.752706
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0128_noti_to_sms_sender
Create Date: 2017-10-26 14:33:41.336861 Create Date: 2017-10-26 14:33:41.336861
""" """
from alembic import op from alembic import op
revision = "0129_add_email_auth_permission" revision = "0129_add_email_auth_permission"
@@ -5,6 +5,7 @@ Revises: 0130_service_email_reply_to_row
Create Date: 2017-10-27 16:19:51.458863 Create Date: 2017-10-27 16:19:51.458863
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0131_user_auth_types
Create Date: 2017-11-03 11:07:40.537006 Create Date: 2017-11-03 11:07:40.537006
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0133_set_services_sms_prefix
Create Date: 2017-11-03 13:52:59.715203 Create Date: 2017-11-03 13:52:59.715203
""" """
from datetime import datetime from datetime import datetime
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0134_add_email_2fa_template
Create Date: 2017-11-07 14:35:04.798561 Create Date: 2017-11-07 14:35:04.798561
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0135_stats_template_usage
Create Date: 2017-11-08 11:49:05.773974 Create Date: 2017-11-08 11:49:05.773974
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0136_user_mobile_nullable
Create Date: 2017-11-08 10:15:07.039227 Create Date: 2017-11-08 10:15:07.039227
""" """
from alembic import op from alembic import op
revision = "0137_notification_template_hist" revision = "0137_notification_template_hist"
@@ -5,6 +5,7 @@ Revises: 0137_notification_template_hist
Create Date: 2017-11-06 15:44:59.471977 Create Date: 2017-11-06 15:44:59.471977
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0138_sms_sender_nullable.py
Create Date: 2017-11-10 21:42:59.715203 Create Date: 2017-11-10 21:42:59.715203
""" """
import uuid import uuid
from datetime import datetime from datetime import datetime
@@ -5,6 +5,7 @@ Revises: 0139_migrate_sms_allowance_data
Create Date: 2017-11-07 13:04:04.077142 Create Date: 2017-11-07 13:04:04.077142
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from flask import current_app from flask import current_app
@@ -5,6 +5,7 @@ Revises: 0140_sms_prefix_non_nullable
Create Date: 2017-11-20 11:35:24.402021 Create Date: 2017-11-20 11:35:24.402021
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0141_remove_unused
Create Date: 2017-11-15 14:39:13.657666 Create Date: 2017-11-15 14:39:13.657666
""" """
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0142_validate_constraint
Create Date: 2017-11-21 10:42:25.045444 Create Date: 2017-11-21 10:42:25.045444
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0143_remove_reply_to
Create Date: 2017-11-17 15:42:16.401229 Create Date: 2017-11-17 15:42:16.401229
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0144_template_service_letter
Create Date: 2017-11-22 14:23:48.806781 Create Date: 2017-11-22 14:23:48.806781
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0145_add_notification_reply_to
Create Date: 2017-11-28 15:13:48.730554 Create Date: 2017-11-28 15:13:48.730554
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0146_add_service_callback_api
Create Date: 2017-11-30 15:48:44.588438 Create Date: 2017-11-30 15:48:44.588438
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0148_add_letters_as_pdf_svc_perm
Create Date: 2017-12-04 12:13:35.268712 Create Date: 2017-12-04 12:13:35.268712
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0149_add_crown_to_services
Create Date: 2017-12-01 16:49:51.178455 Create Date: 2017-12-01 16:49:51.178455
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0152_kill_service_free_fragments
Create Date: 2018-01-05 17:04:20.596271 Create Date: 2018-01-05 17:04:20.596271
""" """
from alembic import op from alembic import op
revision = "0156_set_temp_letter_contact" revision = "0156_set_temp_letter_contact"
@@ -5,6 +5,7 @@ Revises: 0156_set_temp_letter_contact
Create Date: 2018-01-08 16:13:25.733336 Create Date: 2018-01-08 16:13:25.733336
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0157_add_rate_limit_to_service
Create Date: 2018-01-09 14:33:08.313893 Create Date: 2018-01-09 14:33:08.313893
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0159_add_historical_redact
Create Date: 2018-01-30 15:35:12.016574 Create Date: 2018-01-30 15:35:12.016574
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
+1
View File
@@ -5,6 +5,7 @@ Revises: 0161_email_branding
Create Date: 2018-02-06 17:08:11.879844 Create Date: 2018-02-06 17:08:11.879844
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0162_remove_org
Create Date: 2018-02-07 14:03:00.804849 Create Date: 2018-02-07 14:03:00.804849
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0163_add_new_org_model
Create Date: 2018-02-09 17:58:34.617206 Create Date: 2018-02-09 17:58:34.617206
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0164_add_organisation_to_service
Create Date: 2018-02-14 17:25:11.747996 Create Date: 2018-02-14 17:25:11.747996
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0167_add_precomp_letter_svc_perm
Create Date: 2018-02-21 14:05:04.448977 Create Date: 2018-02-21 14:05:04.448977
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0168_hidden_templates
Create Date: 2018-02-21 14:05:04.448977 Create Date: 2018-02-21 14:05:04.448977
""" """
from alembic import op from alembic import op
revision = "0169_hidden_templates_nullable" revision = "0169_hidden_templates_nullable"
@@ -5,6 +5,7 @@ Revises: 0169_hidden_templates_nullable
Create Date: 2018-02-21 14:05:04.448977 Create Date: 2018-02-21 14:05:04.448977
""" """
from alembic import op from alembic import op
revision = "0170_hidden_non_nullable" revision = "0170_hidden_non_nullable"
@@ -5,6 +5,7 @@ Revises: 0170_hidden_non_nullable
Create Date: 2018-02-16 14:16:43.618062 Create Date: 2018-02-16 14:16:43.618062
""" """
from datetime import datetime from datetime import datetime
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0171_add_org_invite_template
Create Date: 2018-02-28 17:09:56.619803 Create Date: 2018-02-28 17:09:56.619803
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0172_deprioritise_examples
Create Date: 2018-03-01 11:53:32.964256 Create Date: 2018-03-01 11:53:32.964256
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0173_create_daily_sorted_letter
Create Date: 2018-03-07 12:21:53.098887 Create Date: 2018-03-07 12:21:53.098887
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0174_add_billing_facts
Create Date: 2018-03-12 10:27:09.050837 Create Date: 2018-03-12 10:27:09.050837
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0175_drop_job_statistics_table
Create Date: 2018-03-12 16:54:30.663897 Create Date: 2018-03-12 16:54:30.663897
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0176_alter_billing_columns
Create Date: 2018-02-21 14:05:04.448977 Create Date: 2018-02-21 14:05:04.448977
""" """
from alembic import op from alembic import op
revision = "0177_add_virus_scan_statuses" revision = "0177_add_virus_scan_statuses"
+1
View File
@@ -5,6 +5,7 @@ Revises: 0177_add_virus_scan_statuses
Create Date: 2018-03-14 16:15:01.886998 Create Date: 2018-03-14 16:15:01.886998
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0178_add_filename
Create Date: 2018-03-13 14:52:40.413474 Create Date: 2018-03-13 14:52:40.413474
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0179_billing_primary_const
Create Date: 2018-03-21 13:41:26.203712 Create Date: 2018-03-21 13:41:26.203712
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0182_add_upload_document_perm
Create Date: 2018-03-25 21:23:32.403212 Create Date: 2018-03-25 21:23:32.403212
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0183_alter_primary_key
Create Date: 2018-03-28 16:05:54.648645 Create Date: 2018-03-28 16:05:54.648645
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0184_alter_primary_key_1
Create Date: 2018-04-10 16:35:41.824981 Create Date: 2018-04-10 16:35:41.824981
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0185_add_is_active_to_reply_tos
Create Date: 2018-04-27 16:35:41.824981 Create Date: 2018-04-27 16:35:41.824981
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0186_rename_is_active_columns
Create Date: 2018-05-03 10:10:41.824981 Create Date: 2018-05-03 10:10:41.824981
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0188_add_ft_notification_status
Create Date: 2018-05-10 14:57:52.589773 Create Date: 2018-05-10 14:57:52.589773
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0189_ft_billing_data
Create Date: 2018-05-21 14:24:27.229511 Create Date: 2018-05-21 14:24:27.229511
""" """
from alembic import op from alembic import op
revision = "0191_ft_billing_pkey" revision = "0191_ft_billing_pkey"
@@ -5,6 +5,7 @@ Revises: 0191_ft_billing_pkey
Create Date: 2018-05-21 15:18:43.871256 Create Date: 2018-05-21 15:18:43.871256
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0192_drop_provider_statistics
Create Date: 2018-05-22 10:23:21.937262 Create Date: 2018-05-22 10:23:21.937262
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0193_add_ft_billing_timestamps
Create Date: 2018-05-22 14:34:27.852096 Create Date: 2018-05-22 14:34:27.852096
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0194_ft_billing_created_at
Create Date: 2018-05-22 16:01:53.269137 Create Date: 2018-05-22 16:01:53.269137
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0195_ft_notification_timestamps
Create Date: 2018-05-31 14:31:36.649544 Create Date: 2018-05-31 14:31:36.649544
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
@@ -5,6 +5,7 @@ Revises: 0196_complaints_table
Create Date: 2018-05-31 15:01:32.977620 Create Date: 2018-05-31 15:01:32.977620
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
@@ -5,6 +5,7 @@ Revises: 0203_fix_old_incomplete_jobs
Create Date: 2018-07-10 11:22:01.761829 Create Date: 2018-07-10 11:22:01.761829
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql

Some files were not shown because too many files have changed in this diff Show More