Skip to content

Commit

Permalink
Merge branch 'main' into renovate/all-non-major-github-action
Browse files Browse the repository at this point in the history
  • Loading branch information
jzbahrai authored Apr 29, 2024
2 parents 307e7cc + 5b29366 commit 70a972e
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 100 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ smoke-test:

.PHONY: run
run: ## Run the web app
flask run -p 6011 --host=0.0.0.0
poetry run flask run -p 6011 --host=0.0.0.0

.PHONY: run-celery-local
run-celery-local: ## Run the celery workers with all the queues
./scripts/run_celery_local.sh
poetry run ./scripts/run_celery_local.sh

.PHONY: run-celery-local-filtered
run-celery-local-filtered: ## Run the celery workers with all queues but filter out common scheduled tasks
./scripts/run_celery_local.sh 2>&1 >/dev/null | grep -iEv 'beat|in-flight-to-inbox|run-scheduled-jobs|check-job-status'
poetry run ./scripts/run_celery_local.sh 2>&1 >/dev/null | grep -iEv 'beat|in-flight-to-inbox|run-scheduled-jobs|check-job-status'

.PHONY: run-celery-purge
run-celery-purge: ## Purge the celery queues
./scripts/run_celery_purge.sh
poetry run ./scripts/run_celery_purge.sh

.PHONY: run-db
run-db: ## psql to access dev database
Expand Down
73 changes: 1 addition & 72 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,78 +17,7 @@ Contains:
For any issues during the following instructions, make sure to review the
**Frequent problems** section toward the end of the document.

### Local installation instruction

#### On OS X:

1. Install PyEnv with Homebrew. This will preserve your sanity.

`brew install pyenv`

2. Install Python 3.10.8 or whatever is the latest

`pyenv install 3.10.8`

3. If you expect no conflicts, set `3.10.8` as you default

`pyenv global 3.10.8`

4. Ensure it installed by running

`python --version`

if it did not, take a look here: https://github.com/pyenv/pyenv/issues/660

5. Install `poetry`:

`pip install poetry==1.3.2`

6. Restart your terminal and make your virtual environtment:

`poetry env use $(which python)`

8. Verify that the environment was created and activated by poetry

`poetry env list`

9. Install [Postgres.app](http://postgresapp.com/).

10. Create the database for the application

`createdb --user=postgres notification_api`

11. Install the required environment variables via our LastPast Vault

Within the team's *LastPass Vault*, you should find corresponding folders for this
project containing the `.env` content that you should copy in your project root folder. This
will grant the application necessary access to our internal infrastructure.

If you don't have access to our *LastPass Vault* (as you evaluate our notification
platform for example), you will find a sane set of defaults exists in the `.env.example`
file. Copy that file to `.env` and customize it to your needs.

12. Install all dependencies

`poetry install`

1. Generate the version file ?!?

`make generate-version-file`

14. Run all DB migrations

`flask db upgrade`

15. Run the service

`make run`

15a. To test

`poetry install --with test`

`make test`

### Local installation instruction (Use Dev Containers)
#### In a [VS Code devcontainer](https://code.visualstudio.com/docs/remote/containers-tutorial)

1. Install VS Code
Expand Down
20 changes: 6 additions & 14 deletions app/celery/service_callback_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,31 +65,23 @@ def _send_data_to_service_callback_api(self, data, service_callback_url, token,
data=json.dumps(data),
headers={
"Content-Type": "application/json",
"Authorization": "Bearer {}".format(token),
"Authorization": f"Bearer {token}",
},
timeout=5,
)
current_app.logger.info(
"{} sending {} to {}, response {}".format(
function_name,
notification_id,
service_callback_url,
response.status_code,
)
f"{function_name} sending {notification_id} to {service_callback_url}, response {response.status_code}"
)
response.raise_for_status()
except RequestException as e:
current_app.logger.warning(
"{} request failed for notification_id: {} and url: {}. exc: {}".format(
function_name, notification_id, service_callback_url, e
)
f"{function_name} request failed for notification_id: {notification_id} and url: {service_callback_url}. exc: {e}"
)
if not isinstance(e, HTTPError) or e.response.status_code >= 500:
# Retry if the response status code is server-side or 429 (too many requests).
if not isinstance(e, HTTPError) or e.response.status_code >= 500 or e.response.status_code == 429:
try:
self.retry(queue=QueueNames.CALLBACKS_RETRY)
except self.MaxRetriesExceededError:
current_app.logger.warning(
"Retry: {} has retried the max num of times for callback url {} and notification_id: {}".format(
function_name, service_callback_url, notification_id
)
"Retry: {function_name} has retried the max num of times for callback url {service_callback_url} and notification_id: {notification_id}"
)
4 changes: 4 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ class EmailBranding(BaseModel):
UUID(as_uuid=True), db.ForeignKey("organisation.id", ondelete="SET NULL"), index=True, nullable=True
)
organisation = db.relationship("Organisation", back_populates="email_branding", foreign_keys=[organisation_id])
alt_text_en = db.Column(db.String(), nullable=True)
alt_text_fr = db.Column(db.String(), nullable=True)

def serialize(self) -> dict:
serialized = {
Expand All @@ -290,6 +292,8 @@ def serialize(self) -> dict:
"text": self.text,
"brand_type": self.brand_type,
"organisation_id": str(self.organisation_id) if self.organisation_id else "",
"alt_text_en": self.alt_text_en,
"alt_text_fr": self.alt_text_fr,
}

return serialized
Expand Down
34 changes: 34 additions & 0 deletions migrations/versions/0446_add_alt_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
Revision ID: 0446_add_alt_text.py
Revises: 0445_add_org_id_branding.py
Create Date: 2024-04-23
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy import text

revision = "0446_add_alt_text"
down_revision = "0445_add_org_id_branding"


def upgrade():
table_description = op.get_bind().execute(
text("SELECT * FROM information_schema.columns WHERE table_name = 'email_branding'")
)

# Check if the column exists
if "alt_text_en" not in [column["column_name"] for column in table_description]:
op.add_column(
"email_branding",
sa.Column("alt_text_en", sa.String(), nullable=True),
)
if "alt_text_fr" not in [column["column_name"] for column in table_description]:
op.add_column(
"email_branding",
sa.Column("alt_text_fr", sa.String(), nullable=True),
)


def downgrade():
op.drop_column("email_branding", "alt_text_fr")
op.drop_column("email_branding", "alt_text_en")
97 changes: 97 additions & 0 deletions migrations/versions/0447_update_verify_code_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Revision ID: 0447_update_verify_code_template
Revises: 0446_add_alt_text
Create Date: 2023-10-05 00:00:00
"""
from datetime import datetime

from alembic import op
from flask import current_app

revision = "0447_update_verify_code_template"
down_revision = "0446_add_alt_text"

near_content = "\n".join(
[
"[[en]]",
"Hi ((name)),",
"",
"Here is your security code to log in to GC Notify:",
"",
"^ **((verify_code))**",
"[[/en]]",
"",
"---",
"",
"[[fr]]",
"Bonjour ((name)),",
"",
"Voici votre code de sécurité pour vous connecter à Notification GC:",
"",
"^ **((verify_code))**",
"[[/fr]]",
]
)


templates = [
{
"id": current_app.config["EMAIL_2FA_TEMPLATE_ID"],
"template_type": "email",
"subject": "Sign in | Connectez-vous",
"content": near_content,
"process_type": "priority",
},
]


def upgrade():
conn = op.get_bind()

for template in templates:
current_version = conn.execute("select version from templates where id='{}'".format(template["id"])).fetchone()
name = conn.execute("select name from templates where id='{}'".format(template["id"])).fetchone()
template["version"] = current_version[0] + 1
template["name"] = name[0]

template_update = """
UPDATE templates SET content = '{}', subject = '{}', version = '{}', updated_at = '{}'
WHERE id = '{}'
"""
template_history_insert = """
INSERT INTO templates_history (id, name, template_type, created_at, content, archived, service_id, subject,
created_by_id, version, process_type, hidden)
VALUES ('{}', '{}', '{}', '{}', '{}', False, '{}', '{}', '{}', {}, '{}', false)
"""

for template in templates:
op.execute(
template_update.format(
template["content"],
template["subject"],
template["version"],
datetime.utcnow(),
template["id"],
)
)

op.execute(
template_history_insert.format(
template["id"],
template["name"],
template["template_type"],
datetime.utcnow(),
template["content"],
current_app.config["NOTIFY_SERVICE_ID"],
template["subject"],
current_app.config["NOTIFY_USER_ID"],
template["version"],
template["process_type"],
)
)


def downgrade():
pass
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Werkzeug = "2.3.7"
MarkupSafe = "2.1.4"
# REVIEW: v2 is using sha512 instead of sha1 by default (in v1)
itsdangerous = "2.1.2"
notifications-utils = { git = "https://github.com/cds-snc/notifier-utils.git", tag = "52.1.5" }
notifications-utils = { git = "https://github.com/cds-snc/notifier-utils.git", tag = "52.2.0" }
# rsa = "4.9 # awscli 1.22.38 depends on rsa<4.8
typing-extensions = "4.7.1"
greenlet = "2.0.2"
Expand Down
2 changes: 1 addition & 1 deletion tests-perf/locust/locust.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ locustfile = tests-perf/locust/locust-notifications.py
host = https://api.staging.notification.cdssandbox.xyz
users = 3000
spawn-rate = 20
run-time = 5m
run-time = 10m

# headless = true
# master = true
Expand Down
7 changes: 4 additions & 3 deletions tests/app/celery/test_service_callback_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_signed_da


@pytest.mark.parametrize("notification_type", ["email", "letter", "sms"])
def test__send_data_to_service_callback_api_retries_if_request_returns_500_with_signed_data(
notify_db_session, mocker, notification_type
@pytest.mark.parametrize("status_code", [429, 500, 503])
def test__send_data_to_service_callback_api_retries_if_request_returns_error_code_with_signed_data(
notify_db_session, mocker, notification_type, status_code
):
callback_api, template = _set_up_test_data(notification_type, "delivery_status")
datestr = datetime(2017, 6, 20)
Expand All @@ -107,7 +108,7 @@ def test__send_data_to_service_callback_api_retries_if_request_returns_500_with_
signed_data = _set_up_data_for_status_update(callback_api, notification)
mocked = mocker.patch("app.celery.service_callback_tasks.send_delivery_status_to_service.retry")
with requests_mock.Mocker() as request_mock:
request_mock.post(callback_api.url, json={}, status_code=500)
request_mock.post(callback_api.url, json={}, status_code=status_code)
send_delivery_status_to_service(notification.id, signed_status_update=signed_data)

assert mocked.call_count == 1
Expand Down
8 changes: 7 additions & 1 deletion tests/app/email_branding/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ def test_get_email_branding_options_filter_org(admin_request, notify_db, notify_


def test_get_email_branding_by_id(admin_request, notify_db, notify_db_session):
email_branding = EmailBranding(colour="#FFFFFF", logo="/path/image.png", name="Some Org", text="My Org")
email_branding = EmailBranding(
colour="#FFFFFF", logo="/path/image.png", name="Some Org", text="My Org", alt_text_en="hello world"
)
notify_db.session.add(email_branding)
notify_db.session.commit()

Expand All @@ -57,13 +59,17 @@ def test_get_email_branding_by_id(admin_request, notify_db, notify_db_session):
"text",
"brand_type",
"organisation_id",
"alt_text_en",
"alt_text_fr",
}
assert response["email_branding"]["colour"] == "#FFFFFF"
assert response["email_branding"]["logo"] == "/path/image.png"
assert response["email_branding"]["name"] == "Some Org"
assert response["email_branding"]["text"] == "My Org"
assert response["email_branding"]["id"] == str(email_branding.id)
assert response["email_branding"]["brand_type"] == str(email_branding.brand_type)
assert response["email_branding"]["alt_text_en"] == "hello world"
assert response["email_branding"]["alt_text_fr"] is None


def test_post_create_email_branding(admin_request, notify_db_session):
Expand Down

0 comments on commit 70a972e

Please sign in to comment.