diff --git a/.github/workflows/e2e-smoke-dev.yaml b/.github/workflows/e2e-smoke-dev.yaml index c7a375a2..8fb541bc 100644 --- a/.github/workflows/e2e-smoke-dev.yaml +++ b/.github/workflows/e2e-smoke-dev.yaml @@ -26,7 +26,7 @@ jobs: # the real prod send path runs (outbox -> dispatch -> /api/transactional/send # -> audit), but with Datamailer's dry_run flag the render is returned inline # and nothing is delivered. For this to be SAFE, the dev DEPLOYMENT itself - # must run with DATAMAILER_TRANSACTIONAL_DRY_RUN=1 (set on the CMP dev ECS + # must run with RELAY_TRANSACTIONAL_DRY_RUN=1 (set on the CMP dev ECS # task definition, provisioned via Terraform in DataTalksClub/infra-terraform # -- it is not configured from this repo). Without it, dev would send real # email during the smoke run; the email tests xfail if no audit appears. diff --git a/README.md b/README.md index 657bc251..d6f435c8 100644 --- a/README.md +++ b/README.md @@ -194,30 +194,30 @@ Response: In local development, the version comes from the `VERSION` environment variable and falls back to `local-development-build-version-not-configured`. -## Datamailer +## Relay -The platform can sync created users and course enrollments to Datamailer. +The platform syncs users and course enrollments and sends email through Relay. Set all required environment variables to enable the integration: ```bash -export DATAMAILER_URL="https://datamailer.dtcdev.click" -export DATAMAILER_API_KEY="" -export DATAMAILER_CLIENT="dtc-courses" -export DATAMAILER_AUDIENCE="dtc-courses" -export DATAMAILER_FROM_EMAIL="courses" +export RELAY_URL="https://relay.dtcdev.click" +export RELAY_API_KEY="" +export RELAY_CLIENT="dtc-courses" +export RELAY_AUDIENCE="dtc-courses" +export RELAY_FROM_EMAIL="courses" ``` Optional settings: ```bash -export DATAMAILER_STRICT="0" -export DATAMAILER_SYNC_ON_USER_CREATE="1" +export RELAY_STRICT="0" +export RELAY_SYNC_ON_USER_CREATE="1" ``` -With `DATAMAILER_STRICT=0`, Datamailer API failures are logged and don't -break signup or enrollment flows. Set `DATAMAILER_STRICT=1` only when those -flows should fail on Datamailer errors. +With `RELAY_STRICT=0`, Relay API failures are logged and don't +break signup or enrollment flows. Set `RELAY_STRICT=1` only when those +flows should fail on Relay errors. `PUBLIC_BASE_URL` is used for links in delivered transactional email. Set it when sending email from a local server so messages contain public HTTPS links @@ -230,17 +230,17 @@ export PUBLIC_BASE_URL="https://dev.courses.datatalks.club" ## Non-delivering email verification (dry run) To exercise the transactional email path without delivering anything, set -`DATAMAILER_TRANSACTIONAL_DRY_RUN=1`. CMP then adds Datamailer's `dry_run` flag +`RELAY_TRANSACTIONAL_DRY_RUN=1`. CMP then adds Relay's `dry_run` flag to every `POST /api/transactional/send`: the identical prod pipeline runs -(outbox -> dispatch -> send -> `DatamailerSendAudit`), but Datamailer renders +(outbox -> dispatch -> send -> `DatamailerSendAudit`), but Relay renders the email and returns it inline without sending, queuing, or persisting -anything. +anything. `DatamailerSendAudit` is retained as a historical database model name; +it does not select or configure the old service. The rendered subject/bodies land in the audit's `response_payload["rendered"]`, which CMP exposes over HTTP at `GET /api/datamailer/send-audits` (filter by `email` / `template_key` / `idempotency_key`). The e2e smoke suite -uses this to verify confirmation emails safely. This replaces the old local -Datamailer capture/testbed compose stack. +uses this to verify confirmation emails safely. Deadline reminder emails are triggered by a short CMP management command: @@ -254,8 +254,8 @@ task. The recurring trigger (an EventBridge/CloudWatch rule targeting invocation role are provisioned via Terraform in `DataTalksClub/infra-terraform`, alongside the CMP ECS service. CMP doesn't configure the schedule. -The scheduled task exits after reconciling Datamailer recipient lists and -triggering Datamailer list sends. Datamailer handles per-recipient delivery +The scheduled task exits after reconciling Relay recipient lists and +triggering Relay list sends. Relay handles per-recipient delivery asynchronously. Every event is attempted even if an earlier one fails; each failure is written @@ -268,16 +268,16 @@ curl -s -H "Authorization: Token $TOKEN" \ ``` Bulk sends post the whole recipient list inline, so they use a longer HTTP -timeout than transactional sends (`DATAMAILER_TIMEOUT_SECONDS`, default 60). -Don't lower it: hanging up mid-request makes Datamailer abandon the dispatch it +timeout than transactional sends (`RELAY_TIMEOUT_SECONDS`, default 60). +Don't lower it: hanging up mid-request makes Relay abandon the dispatch it is partway through, stranding every not-yet-sent message at `queued` with no error and no retry, while CMP records a failure for work that partly succeeded. -Datamailer transactional template keys are stable code-level constants in CMP. +Relay transactional template keys are stable code-level constants in CMP. Don't configure one environment variable per template. -The CMP/Datamailer integration, including the conceptual design and API -reference, is documented in +The CMP/Relay integration, including the conceptual design and API reference, +is documented in the historically named [`docs/datamailer-integration.md`](docs/datamailer-integration.md). ## API Data Access diff --git a/accounts/tests_account_settings_base.py b/accounts/tests_account_settings_base.py index 3cb65bd9..382152b0 100644 --- a/accounts/tests_account_settings_base.py +++ b/accounts/tests_account_settings_base.py @@ -1,12 +1,12 @@ from django.test import override_settings from accounts.tests_base import ( - DATAMAILER_DISABLED_SETTINGS, + RELAY_DISABLED_SETTINGS, AccountCourseTestCase, ) -@override_settings(**DATAMAILER_DISABLED_SETTINGS) +@override_settings(**RELAY_DISABLED_SETTINGS) class AccountSettingsViewTestBase(AccountCourseTestCase): def account_settings_profile_payload(self): payload = { diff --git a/accounts/tests_base.py b/accounts/tests_base.py index c5c0ff79..bee0d64e 100644 --- a/accounts/tests_base.py +++ b/accounts/tests_base.py @@ -3,11 +3,11 @@ from accounts.models import CustomUser from courses.models import Course, Enrollment -DATAMAILER_DISABLED_SETTINGS = { - "DATAMAILER_URL": "", - "DATAMAILER_API_KEY": "", - "DATAMAILER_CLIENT": "", - "DATAMAILER_AUDIENCE": "", +RELAY_DISABLED_SETTINGS = { + "RELAY_URL": "", + "RELAY_API_KEY": "", + "RELAY_CLIENT": "", + "RELAY_AUDIENCE": "", } diff --git a/accounts/tests_email_preferences.py b/accounts/tests_email_preferences.py index 91e8004d..48e46472 100644 --- a/accounts/tests_email_preferences.py +++ b/accounts/tests_email_preferences.py @@ -5,12 +5,12 @@ from accounts.models import CustomUser from accounts.tests_base import ( - DATAMAILER_DISABLED_SETTINGS, + RELAY_DISABLED_SETTINGS, AccountCourseTestCase, ) -@override_settings(**DATAMAILER_DISABLED_SETTINGS) +@override_settings(**RELAY_DISABLED_SETTINGS) class AccountEmailPreferencesTestCase(AccountCourseTestCase): @patch( "accounts.views.email_preferences." diff --git a/accounts/tests_enrollment_profile.py b/accounts/tests_enrollment_profile.py index a52d4d40..4d842d72 100644 --- a/accounts/tests_enrollment_profile.py +++ b/accounts/tests_enrollment_profile.py @@ -2,12 +2,12 @@ from django.urls import reverse from accounts.tests_base import ( - DATAMAILER_DISABLED_SETTINGS, + RELAY_DISABLED_SETTINGS, AccountCourseTestCase, ) -@override_settings(**DATAMAILER_DISABLED_SETTINGS) +@override_settings(**RELAY_DISABLED_SETTINGS) class EnrollmentProfileTestCase(AccountCourseTestCase): def enrollment_payload(self, display_public_profile=False): payload = { diff --git a/accounts/tests_timezone.py b/accounts/tests_timezone.py index cc0b9ba7..55c26aca 100644 --- a/accounts/tests_timezone.py +++ b/accounts/tests_timezone.py @@ -4,12 +4,12 @@ from django.urls import reverse from accounts.tests_base import ( - DATAMAILER_DISABLED_SETTINGS, + RELAY_DISABLED_SETTINGS, AccountCourseTestCase, ) -@override_settings(**DATAMAILER_DISABLED_SETTINGS) +@override_settings(**RELAY_DISABLED_SETTINGS) class AccountTimezonePreferenceTestCase(AccountCourseTestCase): def post_timezone_preference(self, payload): url = reverse("update_timezone_preference") diff --git a/api/openapi/data_paths.py b/api/openapi/data_paths.py index 51fc8396..6bc26e82 100644 --- a/api/openapi/data_paths.py +++ b/api/openapi/data_paths.py @@ -151,18 +151,18 @@ ) COURSE_CERTIFICATES_OPERATION = operation(COURSE_CERTIFICATES_DATA) -DATAMAILER_EVENTS_SUCCESS_RESPONSE = schema_response( +RELAY_EVENTS_SUCCESS_RESPONSE = schema_response( "Datamailer event accepted", "DatamailerEventAccepted", ) -DATAMAILER_EVENTS_RESPONSES = { - "200": DATAMAILER_EVENTS_SUCCESS_RESPONSE, +RELAY_EVENTS_RESPONSES = { + "200": RELAY_EVENTS_SUCCESS_RESPONSE, "400": INVALID_EVENT_PAYLOAD_RESPONSE, "401": INVALID_WEBHOOK_TOKEN_RESPONSE, "503": WEBHOOK_NOT_CONFIGURED_RESPONSE, } -DATAMAILER_EVENTS_BODY = schema_request_body("DatamailerEvent") -DATAMAILER_EVENTS_DESCRIPTION = ( +RELAY_EVENTS_BODY = schema_request_body("DatamailerEvent") +RELAY_EVENTS_DESCRIPTION = ( "Webhook used by Datamailer to report hard bounces, " "complaints, subscription changes, skipped/failed sends, and " "message lifecycle events back to CMP for support and audit " @@ -171,25 +171,25 @@ "configured Datamailer webhook token in the Authorization " "bearer token or X-Datamailer-Webhook-Token header." ) -DATAMAILER_EVENTS_DATA = OperationData( +RELAY_EVENTS_DATA = OperationData( "api_datamailer_events", ["Datamailer"], "Receive Datamailer contact event", - DATAMAILER_EVENTS_RESPONSES, - body=DATAMAILER_EVENTS_BODY, + RELAY_EVENTS_RESPONSES, + body=RELAY_EVENTS_BODY, requires_auth=False, - description=DATAMAILER_EVENTS_DESCRIPTION, + description=RELAY_EVENTS_DESCRIPTION, ) -DATAMAILER_EVENTS_OPERATION = operation(DATAMAILER_EVENTS_DATA) +RELAY_EVENTS_OPERATION = operation(RELAY_EVENTS_DATA) -DATAMAILER_SEND_AUDITS_SUCCESS_RESPONSE = schema_response( +RELAY_SEND_AUDITS_SUCCESS_RESPONSE = schema_response( "Datamailer send audits", "DatamailerSendAudits", ) -DATAMAILER_SEND_AUDITS_RESPONSES = { - "200": DATAMAILER_SEND_AUDITS_SUCCESS_RESPONSE, +RELAY_SEND_AUDITS_RESPONSES = { + "200": RELAY_SEND_AUDITS_SUCCESS_RESPONSE, } -DATAMAILER_SEND_AUDITS_PARAMETERS = [ +RELAY_SEND_AUDITS_PARAMETERS = [ { "name": "email", "in": "query", @@ -215,7 +215,7 @@ "schema": {"type": "integer", "default": 25, "maximum": 100}, }, ] -DATAMAILER_SEND_AUDITS_DESCRIPTION = ( +RELAY_SEND_AUDITS_DESCRIPTION = ( "Lists CMP's own Datamailer send-audit rows (one per send attempt " "through the outbox -> dispatch -> /api/transactional/send pipeline). " "Each row's response_payload carries the message summary and, when the " @@ -223,15 +223,15 @@ "Used by the e2e smoke suite to verify the rendered email over HTTP " "without delivering anything. Ordered newest first." ) -DATAMAILER_SEND_AUDITS_DATA = OperationData( +RELAY_SEND_AUDITS_DATA = OperationData( "api_datamailer_send_audits", ["Datamailer"], "List Datamailer send audits", - DATAMAILER_SEND_AUDITS_RESPONSES, - parameters=DATAMAILER_SEND_AUDITS_PARAMETERS, - description=DATAMAILER_SEND_AUDITS_DESCRIPTION, + RELAY_SEND_AUDITS_RESPONSES, + parameters=RELAY_SEND_AUDITS_PARAMETERS, + description=RELAY_SEND_AUDITS_DESCRIPTION, ) -DATAMAILER_SEND_AUDITS_OPERATION = operation(DATAMAILER_SEND_AUDITS_DATA) +RELAY_SEND_AUDITS_OPERATION = operation(RELAY_SEND_AUDITS_DATA) DATA_PATHS_BY_URL_NAME = { "api_health": { @@ -256,9 +256,9 @@ "post": COURSE_CERTIFICATES_OPERATION, }, "api_datamailer_events": { - "post": DATAMAILER_EVENTS_OPERATION, + "post": RELAY_EVENTS_OPERATION, }, "api_datamailer_send_audits": { - "get": DATAMAILER_SEND_AUDITS_OPERATION, + "get": RELAY_SEND_AUDITS_OPERATION, }, } diff --git a/api/views/datamailer_webhook_validation.py b/api/views/datamailer_webhook_validation.py index c4bf7197..007306f2 100644 --- a/api/views/datamailer_webhook_validation.py +++ b/api/views/datamailer_webhook_validation.py @@ -51,7 +51,7 @@ def bearer_token(request): def authenticate_webhook(request): - expected = getattr(settings, "DATAMAILER_WEBHOOK_TOKEN", "") + expected = getattr(settings, "RELAY_WEBHOOK_TOKEN", "") if not expected: return False token = bearer_token(request) @@ -159,7 +159,7 @@ def load_webhook_fields(data): def validate_webhook_configuration(data): - webhook_token = getattr(settings, "DATAMAILER_WEBHOOK_TOKEN", "") + webhook_token = getattr(settings, "RELAY_WEBHOOK_TOKEN", "") if webhook_token: return None error = webhook_error( diff --git a/cadmin/tests/campaign_view_base.py b/cadmin/tests/campaign_view_base.py index b4da6abe..045a7a66 100644 --- a/cadmin/tests/campaign_view_base.py +++ b/cadmin/tests/campaign_view_base.py @@ -3,11 +3,11 @@ from courses.models import Course, RegistrationCampaign, User -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/cadmin/tests/test_campaign_datamailer_views.py b/cadmin/tests/test_campaign_datamailer_views.py index 9f28301e..b232c2be 100644 --- a/cadmin/tests/test_campaign_datamailer_views.py +++ b/cadmin/tests/test_campaign_datamailer_views.py @@ -4,7 +4,7 @@ from django.urls import reverse from cadmin.tests.campaign_view_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, CampaignCadminViewBase, admin_credentials, ) @@ -12,7 +12,7 @@ class CampaignDatamailerCadminViewTests(CampaignCadminViewBase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( @@ -37,7 +37,7 @@ def test_campaign_edit_syncs_datamailer_campaign_draft( self.assertRedirects(response, url) self.assert_campaign_draft_upserted(upsert_campaign) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_campaigns.DatamailerCampaignClient.preview_campaign" ) @@ -71,7 +71,7 @@ def test_campaign_edit_previews_datamailer_campaign( self.assertContains(response, "Preview subject") self.assertContains(response, "Preview text") - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_campaigns.DatamailerCampaignClient.test_send_campaign" ) @@ -102,7 +102,7 @@ def test_campaign_edit_sends_datamailer_campaign_test( expected_recipients, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_campaigns.DatamailerCampaignClient.queue_campaign" ) @@ -129,7 +129,7 @@ def test_campaign_edit_queues_datamailer_campaign( "cmp-registration-llm-zoomcamp" ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_campaigns.DatamailerCampaignClient.cancel_campaign" ) diff --git a/cadmin/views/campaign_datamailer.py b/cadmin/views/campaign_datamailer.py index d4563f0f..4a9e868b 100644 --- a/cadmin/views/campaign_datamailer.py +++ b/cadmin/views/campaign_datamailer.py @@ -18,7 +18,7 @@ ) -DATAMAILER_CAMPAIGN_UPSERT_ACTIONS = { +RELAY_CAMPAIGN_UPSERT_ACTIONS = { "sync", "preview", "test_send", @@ -132,7 +132,7 @@ def cancel_datamailer_campaign_action(request, client, external_key): return None, True -DATAMAILER_CAMPAIGN_ACTION_HANDLERS = { +RELAY_CAMPAIGN_ACTION_HANDLERS = { "sync": sync_datamailer_campaign_action, "preview": preview_datamailer_campaign_action, "test_send": test_send_datamailer_campaign_action, @@ -142,7 +142,7 @@ def cancel_datamailer_campaign_action(request, client, external_key): def run_datamailer_campaign_action(data): - handler = DATAMAILER_CAMPAIGN_ACTION_HANDLERS.get(data.action) + handler = RELAY_CAMPAIGN_ACTION_HANDLERS.get(data.action) if handler: return handler(data.request, data.client, data.external_key) @@ -151,7 +151,7 @@ def run_datamailer_campaign_action(data): def upsert_datamailer_campaign_if_needed(data): - if data.action not in DATAMAILER_CAMPAIGN_UPSERT_ACTIONS: + if data.action not in RELAY_CAMPAIGN_UPSERT_ACTIONS: return payload = registration_campaign_datamailer_payload(data.campaign) diff --git a/cadmin/views/datamailer_operations.py b/cadmin/views/datamailer_operations.py index 83ce75a5..d872aebf 100644 --- a/cadmin/views/datamailer_operations.py +++ b/cadmin/views/datamailer_operations.py @@ -17,7 +17,7 @@ ) -DATAMAILER_RECIPIENT_LIST_KINDS = [ +RELAY_RECIPIENT_LIST_KINDS = [ "registrations", "enrollments", "homework", @@ -26,7 +26,7 @@ "graduates", ] -DATAMAILER_OPERATOR_COMMANDS = ( +RELAY_OPERATOR_COMMANDS = ( { "title": "Bootstrap contacts", "description": "Load active CMP users into Datamailer contacts.", @@ -96,6 +96,6 @@ def datamailer_operations_context(): "send_by_status": send_by_status, "send_by_type": send_by_type, "recent_failed_sends": recent_failed_sends, - "operator_commands": DATAMAILER_OPERATOR_COMMANDS, - "recipient_list_kinds": DATAMAILER_RECIPIENT_LIST_KINDS, + "operator_commands": RELAY_OPERATOR_COMMANDS, + "recipient_list_kinds": RELAY_RECIPIENT_LIST_KINDS, } diff --git a/course_management/datamailer/client.py b/course_management/datamailer/client.py index c8a29c28..eed21b3c 100644 --- a/course_management/datamailer/client.py +++ b/course_management/datamailer/client.py @@ -16,7 +16,7 @@ # Bulk sends post the whole recipient list inline, so they need far longer # than a single transactional message. This is not just a false-failure -# problem: when we hang up early Datamailer abandons the dispatch it was +# problem: when we hang up early Relay abandons the dispatch it was # midway through, and every message it had created but not yet sent stays # "queued" forever with no error and no retry. A July 2026 reminder run # delivered to exactly one recipient -- the first in the list -- and @@ -37,22 +37,22 @@ class DatamailerConfig: @classmethod def from_settings(cls) -> "DatamailerConfig | None": - url = getattr(settings, "DATAMAILER_URL", "") - api_key = getattr(settings, "DATAMAILER_API_KEY", "") - client = getattr(settings, "DATAMAILER_CLIENT", "") - audience = getattr(settings, "DATAMAILER_AUDIENCE", "") - from_email = getattr(settings, "DATAMAILER_FROM_EMAIL", "") + url = getattr(settings, "RELAY_URL", "") + api_key = getattr(settings, "RELAY_API_KEY", "") + client = getattr(settings, "RELAY_CLIENT", "") + audience = getattr(settings, "RELAY_AUDIENCE", "") + from_email = getattr(settings, "RELAY_FROM_EMAIL", "") if not all([url, api_key, client, audience]): return None - strict = getattr(settings, "DATAMAILER_STRICT", False) + strict = getattr(settings, "RELAY_STRICT", False) transactional_dry_run = getattr( - settings, "DATAMAILER_TRANSACTIONAL_DRY_RUN", False + settings, "RELAY_TRANSACTIONAL_DRY_RUN", False ) timeout = getattr( settings, - "DATAMAILER_TIMEOUT_SECONDS", + "RELAY_TIMEOUT_SECONDS", DEFAULT_TIMEOUT_SECONDS, ) normalized_url = url.rstrip("/") diff --git a/course_management/datamailer/recipient_list_imports.py b/course_management/datamailer/recipient_list_imports.py index 7d38abc5..36be4391 100644 --- a/course_management/datamailer/recipient_list_imports.py +++ b/course_management/datamailer/recipient_list_imports.py @@ -38,7 +38,7 @@ def safe_s3_key_part(value): def import_object_key(kind, config, list_key, content_sha256): - import_prefix = getattr(settings, "DATAMAILER_IMPORT_S3_PREFIX", "") + import_prefix = getattr(settings, "RELAY_IMPORT_S3_PREFIX", "") safe_list_key = safe_s3_key_part(list_key) parts = [ import_prefix, @@ -58,17 +58,17 @@ def import_object_key(kind, config, list_key, content_sha256): def import_s3_bucket(): - bucket = getattr(settings, "DATAMAILER_IMPORT_S3_BUCKET", "") + bucket = getattr(settings, "RELAY_IMPORT_S3_BUCKET", "") if not bucket: raise CommandError( - "DATAMAILER_IMPORT_S3_BUCKET must be set when using " + "RELAY_IMPORT_S3_BUCKET must be set when using " "--import-by-reference." ) return bucket def import_s3_client(): - region = getattr(settings, "DATAMAILER_IMPORT_S3_REGION", "") + region = getattr(settings, "RELAY_IMPORT_S3_REGION", "") s3_kwargs = {} if region: s3_kwargs["region_name"] = region @@ -99,7 +99,7 @@ def upload_import_body(upload_body): def presigned_import_url(s3, bucket, key): params = {"Bucket": bucket, "Key": key} expires_in = getattr( - settings, "DATAMAILER_IMPORT_URL_EXPIRES_SECONDS", 3600 + settings, "RELAY_IMPORT_URL_EXPIRES_SECONDS", 3600 ) return s3.generate_presigned_url( "get_object", diff --git a/course_management/datamailer_outbox.py b/course_management/datamailer_outbox.py index 2014d401..b78ab0b1 100644 --- a/course_management/datamailer_outbox.py +++ b/course_management/datamailer_outbox.py @@ -32,7 +32,7 @@ def enqueue_datamailer_outbox_event( should_dispatch = ( data.dispatch_immediately if data.dispatch_immediately is not None - else getattr(settings, "DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY", False) + else getattr(settings, "RELAY_OUTBOX_DISPATCH_IMMEDIATELY", False) ) event_uuid = uuid4() event_id = f"cmp-datamailer-event:{event_uuid}" diff --git a/course_management/datamailer_outbox_senders.py b/course_management/datamailer_outbox_senders.py index 9e9d1ae3..2b679c61 100644 --- a/course_management/datamailer_outbox_senders.py +++ b/course_management/datamailer_outbox_senders.py @@ -42,7 +42,7 @@ def send_contact_erase_event(client, payload): return client.contacts.erase_contact(email) -DATAMAILER_OUTBOX_EVENT_SENDERS = { +RELAY_OUTBOX_EVENT_SENDERS = { "recipient_list.member_upsert": send_recipient_list_member_upsert_event, "recipient_list.member_remove": send_recipient_list_member_remove_event, "recipient_list.members_bulk_upsert": ( @@ -53,7 +53,7 @@ def send_contact_erase_event(client, payload): def send_event(client, event_type: str, payload: dict[str, Any]): - sender = DATAMAILER_OUTBOX_EVENT_SENDERS.get(event_type) + sender = RELAY_OUTBOX_EVENT_SENDERS.get(event_type) if sender is None: raise ValueError( f"Unsupported Datamailer outbox event type: {event_type}" diff --git a/course_management/datamailer_templates/README.md b/course_management/datamailer_templates/README.md index bb7c1786..cbbfbc76 100644 --- a/course_management/datamailer_templates/README.md +++ b/course_management/datamailer_templates/README.md @@ -17,7 +17,7 @@ uv run python manage.py upsert_datamailer_templates --template-key peer-review-a ``` This `PUT`s each template to Datamailer (`/api/transactional/templates/{key}`) -using the `DATAMAILER_*` settings. View them at `/templates/`. +using the `RELAY_*` settings. View them at `/templates/`. ## What triggers each template diff --git a/course_management/settings.py b/course_management/settings.py index f41f746a..3cbf16ca 100644 --- a/course_management/settings.py +++ b/course_management/settings.py @@ -231,42 +231,42 @@ def env_list(name, default): os.getenv("AWS_REGION", os.getenv("AWS_DEFAULT_REGION", "")), ) -DATAMAILER_URL = os.getenv("DATAMAILER_URL", "") -DATAMAILER_API_KEY = os.getenv("DATAMAILER_API_KEY", "") -DATAMAILER_CLIENT = os.getenv("DATAMAILER_CLIENT", "") -DATAMAILER_AUDIENCE = os.getenv("DATAMAILER_AUDIENCE", "") -DATAMAILER_FROM_EMAIL = os.getenv("DATAMAILER_FROM_EMAIL", "") -DATAMAILER_STRICT = os.getenv("DATAMAILER_STRICT", "0") == "1" -DATAMAILER_TIMEOUT_SECONDS = float( - os.getenv("DATAMAILER_TIMEOUT_SECONDS", "60") +RELAY_URL = os.getenv("RELAY_URL", "") +RELAY_API_KEY = os.getenv("RELAY_API_KEY", "") +RELAY_CLIENT = os.getenv("RELAY_CLIENT", "") +RELAY_AUDIENCE = os.getenv("RELAY_AUDIENCE", "") +RELAY_FROM_EMAIL = os.getenv("RELAY_FROM_EMAIL", "") +RELAY_STRICT = os.getenv("RELAY_STRICT", "0") == "1" +RELAY_TIMEOUT_SECONDS = float( + os.getenv("RELAY_TIMEOUT_SECONDS", "60") ) -# When enabled, transactional sends carry Datamailer's "dry_run" flag: the full +# When enabled, transactional sends carry Relay's "dry_run" flag: the full # prod send path runs (outbox -> dispatch -> /api/transactional/send -> audit) but -# Datamailer renders the email and returns it inline without delivering. Used by +# Relay renders the email and returns it inline without delivering. Used by # the e2e smoke suite (and any non-delivering deployment) to mimic prod safely. -DATAMAILER_TRANSACTIONAL_DRY_RUN = ( - os.getenv("DATAMAILER_TRANSACTIONAL_DRY_RUN", "0") == "1" +RELAY_TRANSACTIONAL_DRY_RUN = ( + os.getenv("RELAY_TRANSACTIONAL_DRY_RUN", "0") == "1" ) -DATAMAILER_WEBHOOK_TOKEN = os.getenv("DATAMAILER_WEBHOOK_TOKEN", "") -DATAMAILER_IMPORT_S3_BUCKET = os.getenv("DATAMAILER_IMPORT_S3_BUCKET", "") -DATAMAILER_IMPORT_S3_PREFIX = os.getenv( - "DATAMAILER_IMPORT_S3_PREFIX", "datamailer-imports" +RELAY_WEBHOOK_TOKEN = os.getenv("RELAY_WEBHOOK_TOKEN", "") +RELAY_IMPORT_S3_BUCKET = os.getenv("RELAY_IMPORT_S3_BUCKET", "") +RELAY_IMPORT_S3_PREFIX = os.getenv( + "RELAY_IMPORT_S3_PREFIX", "relay-imports" ).strip("/") -DATAMAILER_IMPORT_URL_EXPIRES_SECONDS_VALUE = os.getenv( - "DATAMAILER_IMPORT_URL_EXPIRES_SECONDS", "3600" +RELAY_IMPORT_URL_EXPIRES_SECONDS_VALUE = os.getenv( + "RELAY_IMPORT_URL_EXPIRES_SECONDS", "3600" ) -DATAMAILER_IMPORT_URL_EXPIRES_SECONDS = int( - DATAMAILER_IMPORT_URL_EXPIRES_SECONDS_VALUE +RELAY_IMPORT_URL_EXPIRES_SECONDS = int( + RELAY_IMPORT_URL_EXPIRES_SECONDS_VALUE ) -DATAMAILER_IMPORT_S3_REGION = os.getenv("DATAMAILER_IMPORT_S3_REGION", "") -DATAMAILER_SYNC_ON_USER_CREATE = ( - os.getenv("DATAMAILER_SYNC_ON_USER_CREATE", "1") == "1" +RELAY_IMPORT_S3_REGION = os.getenv("RELAY_IMPORT_S3_REGION", "") +RELAY_SYNC_ON_USER_CREATE = ( + os.getenv("RELAY_SYNC_ON_USER_CREATE", "1") == "1" ) # Dispatch outbox events inline (True) or defer to the scheduled processor # (False, the production default). Tests set this to True so sync helpers fire # without an explicit process_due_datamailer_outbox call. -DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY = ( - os.getenv("DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY", "0") == "1" +RELAY_OUTBOX_DISPATCH_IMMEDIATELY = ( + os.getenv("RELAY_OUTBOX_DISPATCH_IMMEDIATELY", "0") == "1" ) # Cache configuration diff --git a/courses/management/commands/audit_datamailer_recipient_lists.py b/courses/management/commands/audit_datamailer_recipient_lists.py index 8e5f6931..6c3ff3d6 100644 --- a/courses/management/commands/audit_datamailer_recipient_lists.py +++ b/courses/management/commands/audit_datamailer_recipient_lists.py @@ -96,8 +96,8 @@ def _datamailer_config(self): config = DatamailerConfig.from_settings() if config is None: raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) return config diff --git a/courses/management/commands/datamailer_campaign.py b/courses/management/commands/datamailer_campaign.py index b6c1670d..2b38daba 100644 --- a/courses/management/commands/datamailer_campaign.py +++ b/courses/management/commands/datamailer_campaign.py @@ -174,8 +174,8 @@ def datamailer_config(self): config = DatamailerConfig.from_settings() if config is None: raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) return config diff --git a/courses/management/commands/datamailer_status.py b/courses/management/commands/datamailer_status.py index 02389100..3b7f42e4 100644 --- a/courses/management/commands/datamailer_status.py +++ b/courses/management/commands/datamailer_status.py @@ -35,8 +35,8 @@ def add_arguments(self, parser): def handle(self, *args, **options): if not datamailer_enabled(): raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) writer = DatamailerStatusWriter(self.stdout) diff --git a/courses/management/commands/send_deadline_reminders.py b/courses/management/commands/send_deadline_reminders.py index 03d3f44e..d6c4a87a 100644 --- a/courses/management/commands/send_deadline_reminders.py +++ b/courses/management/commands/send_deadline_reminders.py @@ -36,8 +36,8 @@ def require_datamailer_config(): config = DatamailerConfig.from_settings() if config is None: raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) return config diff --git a/courses/management/commands/sync_datamailer_contacts.py b/courses/management/commands/sync_datamailer_contacts.py index 48248d8d..1738fbde 100644 --- a/courses/management/commands/sync_datamailer_contacts.py +++ b/courses/management/commands/sync_datamailer_contacts.py @@ -185,8 +185,8 @@ def get_datamailer_config(self): config = DatamailerConfig.from_settings() if config is None: raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) return config diff --git a/courses/management/commands/sync_datamailer_recipient_lists.py b/courses/management/commands/sync_datamailer_recipient_lists.py index f2f37004..2880c52f 100644 --- a/courses/management/commands/sync_datamailer_recipient_lists.py +++ b/courses/management/commands/sync_datamailer_recipient_lists.py @@ -225,7 +225,7 @@ def get_datamailer_config(self): config = DatamailerConfig.from_settings() if config is None: raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) return config diff --git a/courses/management/commands/upsert_datamailer_templates.py b/courses/management/commands/upsert_datamailer_templates.py index d25ac432..d528a7e5 100644 --- a/courses/management/commands/upsert_datamailer_templates.py +++ b/courses/management/commands/upsert_datamailer_templates.py @@ -71,8 +71,8 @@ def get_datamailer_config(self): config = DatamailerConfig.from_settings() if config is None: raise CommandError( - "Datamailer is not configured. Set DATAMAILER_URL, " - "DATAMAILER_API_KEY, DATAMAILER_CLIENT, and DATAMAILER_AUDIENCE." + "Datamailer is not configured. Set RELAY_URL, " + "RELAY_API_KEY, RELAY_CLIENT, and RELAY_AUDIENCE." ) return config diff --git a/courses/signals.py b/courses/signals.py index ebd3fe90..585e8e1d 100644 --- a/courses/signals.py +++ b/courses/signals.py @@ -29,7 +29,7 @@ def sync_user_to_datamailer(sender, instance, created, **kwargs): if not created: return - if not getattr(settings, "DATAMAILER_SYNC_ON_USER_CREATE", True): + if not getattr(settings, "RELAY_SYNC_ON_USER_CREATE", True): return callback = partial(sync_contact, instance) diff --git a/courses/tests/datamailer_contact_base.py b/courses/tests/datamailer_contact_base.py index fe906239..ff4eee54 100644 --- a/courses/tests/datamailer_contact_base.py +++ b/courses/tests/datamailer_contact_base.py @@ -9,11 +9,11 @@ from courses.models import Course -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/courses/tests/datamailer_homework_score_base.py b/courses/tests/datamailer_homework_score_base.py index 89d39c09..9cf0e4f5 100644 --- a/courses/tests/datamailer_homework_score_base.py +++ b/courses/tests/datamailer_homework_score_base.py @@ -17,11 +17,11 @@ from courses.models import Course, Enrollment, Homework, Submission -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/courses/tests/datamailer_membership_base.py b/courses/tests/datamailer_membership_base.py index 495b919e..76e3960e 100644 --- a/courses/tests/datamailer_membership_base.py +++ b/courses/tests/datamailer_membership_base.py @@ -21,11 +21,11 @@ ) -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } @@ -37,7 +37,7 @@ class UpsertedRecipientMemberExpectation: list_type: str -@override_settings(DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY=True) +@override_settings(RELAY_OUTBOX_DISPATCH_IMMEDIATELY=True) class DatamailerMembershipBase(TestCase): def process_due_outbox(self): from course_management.datamailer_outbox_runs import ( diff --git a/courses/tests/datamailer_outbox_base.py b/courses/tests/datamailer_outbox_base.py index 53481324..aecd98d2 100644 --- a/courses/tests/datamailer_outbox_base.py +++ b/courses/tests/datamailer_outbox_base.py @@ -21,15 +21,15 @@ from courses.models import Course, Enrollment -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } -@override_settings(DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY=True, **DATAMAILER_SETTINGS) +@override_settings(RELAY_OUTBOX_DISPATCH_IMMEDIATELY=True, **RELAY_SETTINGS) class DatamailerOutboxTestBase(TestCase): def http_error(self, status_code): exc = requests.HTTPError("request failed") diff --git a/courses/tests/datamailer_project_score_base.py b/courses/tests/datamailer_project_score_base.py index ee77110a..a752bf7f 100644 --- a/courses/tests/datamailer_project_score_base.py +++ b/courses/tests/datamailer_project_score_base.py @@ -22,11 +22,11 @@ ) -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/courses/tests/datamailer_recipient_lists_base.py b/courses/tests/datamailer_recipient_lists_base.py index b2114e8f..3780c463 100644 --- a/courses/tests/datamailer_recipient_lists_base.py +++ b/courses/tests/datamailer_recipient_lists_base.py @@ -18,11 +18,11 @@ ) -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/courses/tests/datamailer_settings.py b/courses/tests/datamailer_settings.py index 9c11463b..68ca8ec2 100644 --- a/courses/tests/datamailer_settings.py +++ b/courses/tests/datamailer_settings.py @@ -1,6 +1,6 @@ -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/courses/tests/deadline_reminder_base.py b/courses/tests/deadline_reminder_base.py index 2d2de77f..3aee834e 100644 --- a/courses/tests/deadline_reminder_base.py +++ b/courses/tests/deadline_reminder_base.py @@ -7,11 +7,11 @@ from courses.models import Course, Enrollment -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } diff --git a/courses/tests/test_datamailer_campaign_command.py b/courses/tests/test_datamailer_campaign_command.py index 9e08f188..3129ed73 100644 --- a/courses/tests/test_datamailer_campaign_command.py +++ b/courses/tests/test_datamailer_campaign_command.py @@ -8,11 +8,11 @@ from django.test import TestCase, override_settings -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } @@ -104,7 +104,7 @@ def assert_campaign_actions_ran(self, expectation): ) self.assertIn("queue: ok", command_output) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_datamailer_campaign_command_upserts_and_runs_actions(self): with ( patch( @@ -137,7 +137,7 @@ def test_datamailer_campaign_command_upserts_and_runs_actions(self): ) self.assert_campaign_actions_ran(expectation) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_datamailer_campaign_command_requires_body(self): with self.assertRaisesMessage( CommandError, @@ -153,7 +153,7 @@ def test_datamailer_campaign_command_requires_body(self): *command_args, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_datamailer_campaign_command_requires_category_tag(self): with self.assertRaisesMessage(CommandError, "--category-tag is required."): command_args = [ @@ -170,7 +170,7 @@ def test_datamailer_campaign_command_requires_category_tag(self): *command_args, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_datamailer_campaign_command_rejects_queue_and_cancel(self): with self.assertRaisesMessage( CommandError, @@ -190,7 +190,7 @@ def test_datamailer_campaign_command_rejects_queue_and_cancel(self): *command_args, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("course_management.datamailer.client_campaigns.DatamailerCampaignClient.upsert_campaign") def test_datamailer_campaign_command_wraps_request_errors( self, diff --git a/courses/tests/test_datamailer_certificates.py b/courses/tests/test_datamailer_certificates.py index 089601c5..b3ebb1a8 100644 --- a/courses/tests/test_datamailer_certificates.py +++ b/courses/tests/test_datamailer_certificates.py @@ -16,11 +16,11 @@ from courses.models import Course, Enrollment -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } @@ -151,8 +151,8 @@ def assert_course_graduate_recipient_payload( class DatamailerCertificatePayloadTestCase(TestCase): @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_FROM_EMAIL="courses", + **RELAY_SETTINGS, + RELAY_FROM_EMAIL="courses", PUBLIC_BASE_URL="https://courses.example.com", ) def test_certificate_availability_notification_payload(self): @@ -165,7 +165,7 @@ def test_certificate_availability_notification_payload(self): assert_certificate_availability_payload(self, payload, enrollment) @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) def test_course_graduate_recipient_list_payload_targets_graduated_outcome( @@ -186,7 +186,7 @@ def test_course_graduate_recipient_list_payload_targets_graduated_outcome( class DatamailerCertificateSendTestCase(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" ) @@ -214,7 +214,7 @@ def test_certificate_availability_notification_uses_datamailer_preference_catego bulk_upsert.assert_called_once() send.assert_called_once() - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" ) diff --git a/courses/tests/test_datamailer_client.py b/courses/tests/test_datamailer_client.py index 4d4f21ae..864864f7 100644 --- a/courses/tests/test_datamailer_client.py +++ b/courses/tests/test_datamailer_client.py @@ -19,7 +19,7 @@ class DatamailerClientEndpointTest(TestCase): def datamailer_config(self): return DatamailerConfig( - url="https://datamailer.example.com", + url="https://relay.example.com", api_key="secret-token", client="dtc-courses", audience="dtc-courses", @@ -44,7 +44,7 @@ def assert_datamailer_request(self, expectation): } if expectation.params is not None: kwargs["params"] = expectation.params - expected_url = f"https://datamailer.example.com{expectation.path}" + expected_url = f"https://relay.example.com{expectation.path}" expectation.session.request.assert_called_once_with( expectation.method, expected_url, @@ -82,14 +82,46 @@ def assert_datamailer_method_case(self, method_case): def test_missing_env_disables_datamailer(self): with override_settings( - DATAMAILER_URL="", - DATAMAILER_API_KEY="", - DATAMAILER_CLIENT="", - DATAMAILER_AUDIENCE="", + RELAY_URL="", + RELAY_API_KEY="", + RELAY_CLIENT="", + RELAY_AUDIENCE="", ): enabled = datamailer_enabled() self.assertFalse(enabled) + @override_settings( + RELAY_URL="https://relay.example.com", + RELAY_API_KEY="relay-token", + RELAY_CLIENT="dtc-courses", + RELAY_AUDIENCE="dtc-courses", + RELAY_FROM_EMAIL="courses", + RELAY_STRICT=True, + ) + def test_relay_settings_configure_client(self): + config = DatamailerConfig.from_settings() + + self.assertIsNotNone(config) + self.assertEqual(config.url, "https://relay.example.com") + self.assertEqual(config.api_key, "relay-token") + self.assertEqual(config.client, "dtc-courses") + self.assertEqual(config.audience, "dtc-courses") + self.assertEqual(config.from_email, "courses") + self.assertTrue(config.strict) + + @override_settings( + RELAY_URL="", + RELAY_API_KEY="", + RELAY_CLIENT="", + RELAY_AUDIENCE="", + DATAMAILER_URL="https://datamailer.example.com", + DATAMAILER_API_KEY="legacy-token", + DATAMAILER_CLIENT="dtc-courses", + DATAMAILER_AUDIENCE="dtc-courses", + ) + def test_legacy_datamailer_settings_do_not_configure_client(self): + self.assertFalse(datamailer_enabled()) + def test_client_methods_use_expected_endpoints_and_scope(self): cases = datamailer_method_cases() for case in cases: diff --git a/courses/tests/test_datamailer_contact.py b/courses/tests/test_datamailer_contact.py index 4c57ed4f..b15c2b6f 100644 --- a/courses/tests/test_datamailer_contact.py +++ b/courses/tests/test_datamailer_contact.py @@ -11,13 +11,13 @@ from course_management.datamailer.sync.contacts import sync_contact from courses.models import Course from courses.tests.datamailer_contact_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerContactBase, ) class DatamailerContactTest(DatamailerContactBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_contact_payload_includes_course_subscription_data(self): user, course = self.create_contact_payload_fixture() @@ -38,7 +38,7 @@ def test_contact_tags_for_course_without_trailing_year(self): expected_tags.append("course-cohort-ml-zoomcamp") self.assertEqual(tags, expected_tags) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.upsert_contact" ) @@ -52,7 +52,7 @@ def test_sync_contact_logs_and_continues_on_api_failure( upsert.assert_called_once() - @override_settings(**DATAMAILER_SETTINGS, DATAMAILER_STRICT=True) + @override_settings(**RELAY_SETTINGS, RELAY_STRICT=True) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.upsert_contact" ) diff --git a/courses/tests/test_datamailer_contact_backfill.py b/courses/tests/test_datamailer_contact_backfill.py index 9c3abfc9..b775ace3 100644 --- a/courses/tests/test_datamailer_contact_backfill.py +++ b/courses/tests/test_datamailer_contact_backfill.py @@ -6,13 +6,13 @@ from accounts.models import CustomUser from courses.tests.datamailer_contact_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerContactBase, ) class DatamailerContactBackfillTest(DatamailerContactBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.bulk_import_contacts" ) @@ -34,7 +34,7 @@ def test_contact_backfill_command_bulk_imports_users( self.assert_first_contact_import_payload(bulk_import) self.assert_contact_import_output(out) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.bulk_import_contacts" ) @@ -55,7 +55,7 @@ def test_contact_backfill_command_dry_run_does_not_call_datamailer( self.assertIn("Prepared 1 contact batch(es), 1 contact(s).", output) self.assertIn("batch 1: 1 contact(s)", output) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.bulk_import_contacts" ) diff --git a/courses/tests/test_datamailer_homework_score_send.py b/courses/tests/test_datamailer_homework_score_send.py index 28b7b1e5..a005d3e1 100644 --- a/courses/tests/test_datamailer_homework_score_send.py +++ b/courses/tests/test_datamailer_homework_score_send.py @@ -13,7 +13,7 @@ send_homework_score_notification, ) from courses.tests.datamailer_homework_score_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerHomeworkScoreTestBase, HomeworkScoreListSendExpectation, ) @@ -22,7 +22,7 @@ class DatamailerHomeworkScoreSendSuccessTest( DatamailerHomeworkScoreTestBase ): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListSendClient.send_to_list" ) @@ -63,7 +63,7 @@ def test_send_homework_score_notification_uses_list_send( class DatamailerHomeworkScoreSendFailureTest( DatamailerHomeworkScoreTestBase ): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListSendClient.send_to_list" ) diff --git a/courses/tests/test_datamailer_homework_scores.py b/courses/tests/test_datamailer_homework_scores.py index fe845375..3cc3c6be 100644 --- a/courses/tests/test_datamailer_homework_scores.py +++ b/courses/tests/test_datamailer_homework_scores.py @@ -5,15 +5,15 @@ homework_score_notification_payload, ) from courses.tests.datamailer_homework_score_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerHomeworkScoreTestBase, ) class DatamailerHomeworkScorePayloadTest(DatamailerHomeworkScoreTestBase): @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_FROM_EMAIL="courses", + **RELAY_SETTINGS, + RELAY_FROM_EMAIL="courses", PUBLIC_BASE_URL="https://courses.example.com", ) def test_homework_score_notification_payload_targets_homework_submitters( @@ -34,7 +34,7 @@ def test_homework_score_notification_payload_targets_homework_submitters( self.assert_homework_score_context_urls(payload) self.assert_homework_score_member(member, submission) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_homework_score_notification_payload_dedupes_student_submissions( self, ): @@ -50,7 +50,7 @@ def test_homework_score_notification_payload_dedupes_student_submissions( self.assertEqual(member["email"], "learner@example.com") self.assertEqual(member["metadata"]["total_score"], 9) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_homework_score_notification_includes_submitters(self): homework = self.create_homework() user = self.create_user("learner@example.com") diff --git a/courses/tests/test_datamailer_membership.py b/courses/tests/test_datamailer_membership.py index 330f4abf..ff338d33 100644 --- a/courses/tests/test_datamailer_membership.py +++ b/courses/tests/test_datamailer_membership.py @@ -16,14 +16,14 @@ sync_project_submission_to_datamailer, ) from courses.tests.datamailer_membership_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerMembershipBase, UpsertedRecipientMemberExpectation, ) class DatamailerMembershipTest(DatamailerMembershipBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_enrollment_recipient_list_payload_targets_course_enrolled( self, ): @@ -54,7 +54,7 @@ def test_enrollment_recipient_list_payload_targets_course_enrolled( enrollment.pk, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) @@ -84,7 +84,7 @@ def test_sync_enrollment_adds_contact_and_enrolled_member( f"user:{user.pk}", ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) @@ -115,7 +115,7 @@ def test_sync_homework_submission_adds_submitter_member( ) self.assert_upserted_recipient_member(expectation) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) diff --git a/courses/tests/test_datamailer_membership_outcomes.py b/courses/tests/test_datamailer_membership_outcomes.py index f2faab05..081bcc74 100644 --- a/courses/tests/test_datamailer_membership_outcomes.py +++ b/courses/tests/test_datamailer_membership_outcomes.py @@ -6,13 +6,13 @@ sync_project_passed_outcome_to_datamailer, ) from courses.tests.datamailer_membership_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerMembershipBase, ) class DatamailerMembershipOutcomeTest(DatamailerMembershipBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) @@ -31,7 +31,7 @@ def test_sync_project_passed_outcome_upserts_passed_member( upsert_contact.assert_called_once() self.assert_project_passed_member_upserted(upsert_member, project) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.remove" ) diff --git a/courses/tests/test_datamailer_membership_removals.py b/courses/tests/test_datamailer_membership_removals.py index 62332100..0d03bbea 100644 --- a/courses/tests/test_datamailer_membership_removals.py +++ b/courses/tests/test_datamailer_membership_removals.py @@ -8,14 +8,14 @@ remove_project_submission_from_datamailer, ) from courses.tests.datamailer_membership_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerMembershipBase, ) class DatamailerMembershipRemovalTest(DatamailerMembershipBase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( @@ -37,7 +37,7 @@ def test_remove_enrollment_removes_enrolled_and_graduate_members( self.assert_enrollment_members_removed(remove_member, course, enrollment) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.remove" ) @@ -60,7 +60,7 @@ def test_remove_homework_submission_deletes_submitter_member( submission, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.remove" ) diff --git a/courses/tests/test_datamailer_outbox_contacts.py b/courses/tests/test_datamailer_outbox_contacts.py index 085e2058..e00d3d77 100644 --- a/courses/tests/test_datamailer_outbox_contacts.py +++ b/courses/tests/test_datamailer_outbox_contacts.py @@ -8,13 +8,13 @@ erase_contact_from_datamailer, ) from courses.tests.datamailer_outbox_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerOutboxTestBase, ) class DatamailerOutboxContactTest(DatamailerOutboxTestBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("course_management.datamailer.client_contacts.DatamailerContactClient.erase_contact") def test_erase_contact_enqueues_outbox_event(self, erase_contact): user = CustomUser.objects.create_user( @@ -28,7 +28,7 @@ def test_erase_contact_enqueues_outbox_event(self, erase_contact): event = DatamailerOutboxEvent.objects.get() self.assert_erase_contact_outbox_event_for_user(event, user) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("course_management.datamailer.client_contacts.DatamailerContactClient.erase_contact") def test_erase_contact_enqueues_outbox_event_for_email( self, erase_contact diff --git a/courses/tests/test_datamailer_outbox_memberships.py b/courses/tests/test_datamailer_outbox_memberships.py index 3d950c33..a9427699 100644 --- a/courses/tests/test_datamailer_outbox_memberships.py +++ b/courses/tests/test_datamailer_outbox_memberships.py @@ -10,13 +10,13 @@ sync_enrollment_to_datamailer, ) from courses.tests.datamailer_outbox_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerOutboxTestBase, ) class DatamailerOutboxMembershipTest(DatamailerOutboxTestBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) @@ -34,8 +34,8 @@ def test_membership_sync_failure_records_retryable_outbox_event( class DatamailerOutboxProcessingTest(DatamailerOutboxTestBase): - @override_settings(DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY=False) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(RELAY_OUTBOX_DISPATCH_IMMEDIATELY=False) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) diff --git a/courses/tests/test_datamailer_outbox_status_commands.py b/courses/tests/test_datamailer_outbox_status_commands.py index a75ec71b..8db6cd63 100644 --- a/courses/tests/test_datamailer_outbox_status_commands.py +++ b/courses/tests/test_datamailer_outbox_status_commands.py @@ -7,13 +7,13 @@ from django.utils import timezone from courses.tests.datamailer_outbox_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerOutboxTestBase, ) class DatamailerOutboxStatusCommandTest(DatamailerOutboxTestBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) diff --git a/courses/tests/test_datamailer_peer_review.py b/courses/tests/test_datamailer_peer_review.py index cecf2ea9..e58bdfd0 100644 --- a/courses/tests/test_datamailer_peer_review.py +++ b/courses/tests/test_datamailer_peer_review.py @@ -29,11 +29,11 @@ ) -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } @@ -219,9 +219,9 @@ def assert_peer_review_send_audit(test_case): class DatamailerPeerReviewPayloadTest(TestCase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", - DATAMAILER_FROM_EMAIL="courses", + RELAY_FROM_EMAIL="courses", ) def test_peer_review_assignment_payload_includes_links_and_deadline(self): project = create_peer_review_assignment_fixture() @@ -299,7 +299,7 @@ def test_preview_peer_review_email_prints_submission_previews(self): class DatamailerPeerReviewNotificationSendTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListSendClient.send_to_list" ) diff --git a/courses/tests/test_datamailer_preferences.py b/courses/tests/test_datamailer_preferences.py index 4844b5b3..84dd6bdb 100644 --- a/courses/tests/test_datamailer_preferences.py +++ b/courses/tests/test_datamailer_preferences.py @@ -8,11 +8,11 @@ update_email_preferences_for_user, ) -from .datamailer_settings import DATAMAILER_SETTINGS +from .datamailer_settings import RELAY_SETTINGS class DatamailerPreferencesTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.contact_preferences" ) @@ -59,7 +59,7 @@ def test_get_email_preferences_for_user_reads_datamailer_categories( category_tags=category_tags, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.update_contact_preferences" ) diff --git a/courses/tests/test_datamailer_project_outcomes.py b/courses/tests/test_datamailer_project_outcomes.py index 80021eec..a3228e81 100644 --- a/courses/tests/test_datamailer_project_outcomes.py +++ b/courses/tests/test_datamailer_project_outcomes.py @@ -10,7 +10,7 @@ send_project_score_notification, ) from courses.tests.datamailer_project_score_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerProjectScoreTestBase, ProjectScoreListSendExpectation, ) @@ -18,7 +18,7 @@ class DatamailerProjectOutcomeTest(DatamailerProjectScoreTestBase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) def test_project_passed_recipient_list_payload_targets_passed_outcome( @@ -48,7 +48,7 @@ def test_project_passed_recipient_list_payload_targets_passed_outcome( self.assertEqual(member["metadata"]["total_score"], 98) self.assertTrue(member["metadata"]["passed"]) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListSendClient.send_to_list" ) diff --git a/courses/tests/test_datamailer_project_scores.py b/courses/tests/test_datamailer_project_scores.py index 6e1db486..f2828f83 100644 --- a/courses/tests/test_datamailer_project_scores.py +++ b/courses/tests/test_datamailer_project_scores.py @@ -5,7 +5,7 @@ project_score_notification_payload, ) from courses.tests.datamailer_project_score_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerProjectScoreTestBase, ScorePayloadExpectation, ) @@ -13,8 +13,8 @@ class DatamailerProjectScoreTest(DatamailerProjectScoreTestBase): @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_FROM_EMAIL="courses", + **RELAY_SETTINGS, + RELAY_FROM_EMAIL="courses", PUBLIC_BASE_URL="https://courses.example.com", ) def test_project_score_notification_payload_targets_project_submitters( @@ -37,7 +37,7 @@ def test_project_score_notification_payload_targets_project_submitters( self.assert_project_score_context(payload) self.assert_project_score_member(member, submission) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_project_score_notification_dedupes_student_submissions(self): project, latest = self.create_duplicate_project_submissions() @@ -49,7 +49,7 @@ def test_project_score_notification_dedupes_student_submissions(self): latest, ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_project_score_notification_includes_submitters(self): project, _ = self.create_project_score_submission() diff --git a/courses/tests/test_datamailer_recipient_list_audit.py b/courses/tests/test_datamailer_recipient_list_audit.py index 53da71ed..d227e4be 100644 --- a/courses/tests/test_datamailer_recipient_list_audit.py +++ b/courses/tests/test_datamailer_recipient_list_audit.py @@ -15,11 +15,11 @@ from courses.models import Course, Enrollment -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } @@ -149,7 +149,7 @@ def assert_recipient_list_audit_repaired(test_case, expectation): class DatamailerRecipientListAuditNoDriftTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.reconcile" ) @@ -182,7 +182,7 @@ def test_recipient_list_audit_reports_no_drift( class DatamailerRecipientListAuditRepairTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.reconcile" ) @@ -214,7 +214,7 @@ def test_recipient_list_audit_can_repair_drift( class DatamailerRecipientListAuditListingErrorTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.list_members" ) @@ -236,7 +236,7 @@ def test_recipient_list_audit_rejects_truncated_member_listing( extra_args=["--limit", "2"], ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.list_members" ) @@ -259,7 +259,7 @@ def test_recipient_list_audit_wraps_member_listing_errors( class DatamailerRecipientListAuditOptionValidationTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_recipient_list_audit_rejects_invalid_options(self): for args, message in ( ( diff --git a/courses/tests/test_datamailer_recipient_list_imports.py b/courses/tests/test_datamailer_recipient_list_imports.py index 90e34384..220b8f5b 100644 --- a/courses/tests/test_datamailer_recipient_list_imports.py +++ b/courses/tests/test_datamailer_recipient_list_imports.py @@ -7,7 +7,7 @@ from course_management.datamailer.keys import course_enrolled_list_key from courses.tests.datamailer_recipient_lists_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerRecipientListCommandTestBase, ImportWaitExpectation, ) @@ -34,10 +34,10 @@ class DatamailerRecipientListImportCreationTest( DatamailerRecipientListCommandTestBase ): @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_IMPORT_S3_BUCKET="cmp-imports", - DATAMAILER_IMPORT_S3_PREFIX="datamailer-test", - DATAMAILER_IMPORT_URL_EXPIRES_SECONDS=900, + **RELAY_SETTINGS, + RELAY_IMPORT_S3_BUCKET="cmp-imports", + RELAY_IMPORT_S3_PREFIX="datamailer-test", + RELAY_IMPORT_URL_EXPIRES_SECONDS=900, ) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListImportClient.create" @@ -83,8 +83,8 @@ class DatamailerRecipientListImportSuccessTest( DatamailerRecipientListCommandTestBase ): @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_IMPORT_S3_BUCKET="cmp-imports", + **RELAY_SETTINGS, + RELAY_IMPORT_S3_BUCKET="cmp-imports", ) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListImportClient.get" @@ -129,8 +129,8 @@ class DatamailerRecipientListImportFailureTest( DatamailerRecipientListCommandTestBase ): @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_IMPORT_S3_BUCKET="cmp-imports", + **RELAY_SETTINGS, + RELAY_IMPORT_S3_BUCKET="cmp-imports", ) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListImportClient.get" @@ -211,8 +211,8 @@ def assert_import_wait_times_out(self, enrollment, job_id): ) @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_IMPORT_S3_BUCKET="cmp-imports", + **RELAY_SETTINGS, + RELAY_IMPORT_S3_BUCKET="cmp-imports", ) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListImportClient.get" @@ -241,7 +241,7 @@ def test_recipient_list_backfill_command_times_out_waiting_for_import( class DatamailerRecipientListImportValidationTest( DatamailerRecipientListCommandTestBase ): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_recipient_list_import_by_reference_requires_s3_bucket(self): self.create_registration( email="student@example.com", @@ -250,7 +250,7 @@ def test_recipient_list_import_by_reference_requires_s3_bucket(self): with self.assertRaisesMessage( CommandError, - "DATAMAILER_IMPORT_S3_BUCKET must be set", + "RELAY_IMPORT_S3_BUCKET must be set", ): call_command( "sync_datamailer_recipient_lists", diff --git a/courses/tests/test_datamailer_recipient_lists.py b/courses/tests/test_datamailer_recipient_lists.py index 8409ae13..d4afee9c 100644 --- a/courses/tests/test_datamailer_recipient_lists.py +++ b/courses/tests/test_datamailer_recipient_lists.py @@ -13,7 +13,7 @@ ) from courses.tests.datamailer_recipient_lists_base import ( BulkUpsertMemberExpectation, - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerRecipientListCommandTestBase, ) @@ -27,7 +27,7 @@ def run_recipient_list_command(*args): class DatamailerRecipientListBulkUpsertTest( DatamailerRecipientListCommandTestBase ): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.bulk_upsert" ) @@ -55,7 +55,7 @@ def test_recipient_list_backfill_command_bulk_upserts_registrations( self.assert_bulk_upsert_member(expectation) self.assert_prepared_one_member(out) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.bulk_upsert" ) @@ -88,7 +88,7 @@ class DatamailerRecipientListProjectPassedTest( DatamailerRecipientListCommandTestBase ): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( @@ -153,7 +153,7 @@ class DatamailerRecipientListGraduateTest( DatamailerRecipientListCommandTestBase ): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( @@ -186,7 +186,7 @@ def test_recipient_list_backfill_command_bulk_upserts_graduates( class DatamailerRecipientListDryRunTest( DatamailerRecipientListCommandTestBase ): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.bulk_upsert" ) @@ -212,7 +212,7 @@ def test_recipient_list_backfill_command_dry_run_does_not_call_datamailer( class DatamailerRecipientListOptionValidationTest( DatamailerRecipientListCommandTestBase ): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) def test_recipient_list_backfill_command_rejects_invalid_options(self): for args, message in ( ( diff --git a/courses/tests/test_datamailer_registration.py b/courses/tests/test_datamailer_registration.py index 59975112..42091176 100644 --- a/courses/tests/test_datamailer_registration.py +++ b/courses/tests/test_datamailer_registration.py @@ -25,12 +25,12 @@ ) from courses.models import Course, CourseRegistration, RegistrationCampaign -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", - "DATAMAILER_OUTBOX_DISPATCH_IMMEDIATELY": True, +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", + "RELAY_OUTBOX_DISPATCH_IMMEDIATELY": True, } @@ -230,7 +230,7 @@ def assert_registration_member_removed( class DatamailerRegistrationConfirmationPayloadTest(TestCase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) def test_registration_confirmation_payload(self): @@ -243,7 +243,7 @@ def test_registration_confirmation_payload(self): class DatamailerRegistrationConfirmationSendTest(TestCase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( @@ -268,7 +268,7 @@ def test_send_registration_confirmation_email_uses_transactional_send( class DatamailerRegistrationMembershipSyncTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.upsert" ) @@ -290,7 +290,7 @@ def test_sync_registration_adds_contact_and_registrant_member( class DatamailerRegistrationMembershipRemovalTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListMemberClient.remove" ) diff --git a/courses/tests/test_datamailer_signals.py b/courses/tests/test_datamailer_signals.py index 7a5292d2..5ed993ff 100644 --- a/courses/tests/test_datamailer_signals.py +++ b/courses/tests/test_datamailer_signals.py @@ -15,16 +15,16 @@ ) -DATAMAILER_SETTINGS = { - "DATAMAILER_URL": "https://datamailer.example.com", - "DATAMAILER_API_KEY": "secret-token", - "DATAMAILER_CLIENT": "dtc-courses", - "DATAMAILER_AUDIENCE": "dtc-courses", +RELAY_SETTINGS = { + "RELAY_URL": "https://relay.example.com", + "RELAY_API_KEY": "secret-token", + "RELAY_CLIENT": "dtc-courses", + "RELAY_AUDIENCE": "dtc-courses", } class DatamailerSignalTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.sync_contact") def test_new_user_syncs_after_commit(self, sync): with self.captureOnCommitCallbacks(execute=True): @@ -32,7 +32,7 @@ def test_new_user_syncs_after_commit(self, sync): sync.assert_called_once_with(user) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.sync_enrollment_recipient_list") def test_new_enrollment_syncs_after_commit(self, sync): user = CustomUser.objects.create(email="student@example.com") @@ -51,7 +51,7 @@ def test_new_enrollment_syncs_after_commit(self, sync): sync.assert_called_once_with(enrollment) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.erase_contact_from_datamailer") def test_deleted_user_erases_contact_after_commit(self, erase_contact): user = CustomUser.objects.create_user( @@ -68,7 +68,7 @@ def test_deleted_user_erases_contact_after_commit(self, erase_contact): email="student@example.com", ) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.remove_registration_recipient_list") def test_deleted_registration_removes_member_after_commit(self, remove): course = Course.objects.create( @@ -94,7 +94,7 @@ def test_deleted_registration_removes_member_after_commit(self, remove): remove.assert_called_once() self.assertEqual(remove.call_args.args[0].pk, registration.pk) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.remove_enrollment_recipient_list") def test_deleted_enrollment_removes_member_after_commit(self, remove): user = CustomUser.objects.create(email="student@example.com") @@ -112,7 +112,7 @@ def test_deleted_enrollment_removes_member_after_commit(self, remove): remove.assert_called_once() self.assertEqual(remove.call_args.args[0].pk, enrollment.pk) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.remove_homework_submission_recipient_list") def test_deleted_homework_submission_removes_member_after_commit( self, @@ -143,7 +143,7 @@ def test_deleted_homework_submission_removes_member_after_commit( remove.assert_called_once() self.assertEqual(remove.call_args.args[0].pk, submission.pk) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.signals.remove_project_submission_recipient_list") def test_deleted_project_submission_removes_member_after_commit( self, diff --git a/courses/tests/test_datamailer_status.py b/courses/tests/test_datamailer_status.py index a3e7d672..610cdc7b 100644 --- a/courses/tests/test_datamailer_status.py +++ b/courses/tests/test_datamailer_status.py @@ -4,7 +4,7 @@ from django.core.management import call_command from django.test import TestCase, override_settings -from .datamailer_settings import DATAMAILER_SETTINGS +from .datamailer_settings import RELAY_SETTINGS def run_datamailer_status_command(*args): @@ -14,7 +14,7 @@ def run_datamailer_status_command(*args): class DatamailerStatusCommandTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("courses.management.commands.datamailer_status.get_email_status") def test_datamailer_status_command_prints_email_history( self, @@ -57,7 +57,7 @@ def test_datamailer_status_command_prints_email_history( self.assertIn("Recent campaign recipients:\n none", output) get_status.assert_called_once_with("student@example.com", limit=10) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "courses.management.commands.datamailer_status." "get_transactional_message_status" diff --git a/courses/tests/test_datamailer_sync_status.py b/courses/tests/test_datamailer_sync_status.py index f6c474dd..0d3d4484 100644 --- a/courses/tests/test_datamailer_sync_status.py +++ b/courses/tests/test_datamailer_sync_status.py @@ -9,11 +9,11 @@ get_transactional_message_status, ) -from .datamailer_settings import DATAMAILER_SETTINGS +from .datamailer_settings import RELAY_SETTINGS class DatamailerContactSyncStatusTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.contact_status" ) @@ -27,7 +27,7 @@ def test_get_contact_status_uses_datamailer_client( self.assertEqual(result, {"exists": True}) contact_status.assert_called_once_with("student@example.com") - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_contacts.DatamailerContactClient.contact_history" ) @@ -41,7 +41,7 @@ def test_get_contact_history_uses_datamailer_client( self.assertEqual(result, {"transactional_messages": []}) contact_history.assert_called_once_with(42, limit=5) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch("course_management.datamailer.sync.status.get_contact_history") @patch("course_management.datamailer.sync.status.get_contact_status") def test_get_email_status_combines_status_and_history( @@ -71,7 +71,7 @@ def test_get_email_status_combines_status_and_history( contact_history.assert_called_once_with(42, limit=5) class DatamailerMessageSyncStatusTest(TestCase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.transactional_message_status" ) diff --git a/courses/tests/test_datamailer_transactional.py b/courses/tests/test_datamailer_transactional.py index 26afb0ef..93cda8b7 100644 --- a/courses/tests/test_datamailer_transactional.py +++ b/courses/tests/test_datamailer_transactional.py @@ -12,13 +12,13 @@ send_transactional_email, ) from courses.tests.datamailer_contact_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DatamailerContactBase, ) class DatamailerTransactionalTest(DatamailerContactBase): - @override_settings(**DATAMAILER_SETTINGS, DATAMAILER_FROM_EMAIL="") + @override_settings(**RELAY_SETTINGS, RELAY_FROM_EMAIL="") @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" ) @@ -34,7 +34,7 @@ def test_send_transactional_email_uses_datamailer_client( self.assert_transactional_send_called(send) self.assert_transactional_send_audit() - @override_settings(**DATAMAILER_SETTINGS, DATAMAILER_FROM_EMAIL="") + @override_settings(**RELAY_SETTINGS, RELAY_FROM_EMAIL="") @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" ) @@ -53,8 +53,8 @@ def test_send_transactional_email_audits_api_failure(self, send): self.assertEqual(audit.error, "network error") @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_FROM_EMAIL="courses", + **RELAY_SETTINGS, + RELAY_FROM_EMAIL="courses", ) @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" @@ -80,8 +80,8 @@ def test_send_transactional_email_adds_configured_from_email( send.assert_called_once_with(expected_payload) @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_FROM_EMAIL="courses", + **RELAY_SETTINGS, + RELAY_FROM_EMAIL="courses", ) @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" @@ -108,9 +108,9 @@ def test_send_transactional_email_keeps_explicit_from_email( send.assert_called_once_with(expected_payload) @override_settings( - **DATAMAILER_SETTINGS, - DATAMAILER_FROM_EMAIL="courses", - DATAMAILER_TRANSACTIONAL_DRY_RUN=True, + **RELAY_SETTINGS, + RELAY_FROM_EMAIL="courses", + RELAY_TRANSACTIONAL_DRY_RUN=True, ) @patch( "course_management.datamailer.client_transactional.DatamailerTransactionalClient.send_transactional" diff --git a/courses/tests/test_deadline_reminder_dry_run.py b/courses/tests/test_deadline_reminder_dry_run.py index b6b28f69..84cc9b02 100644 --- a/courses/tests/test_deadline_reminder_dry_run.py +++ b/courses/tests/test_deadline_reminder_dry_run.py @@ -6,13 +6,13 @@ from courses.models import Homework from courses.tests.deadline_reminder_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DeadlineReminderTestBase, ) class DeadlineReminderDryRunCommandTest(DeadlineReminderTestBase): - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch( "course_management.datamailer.client_recipient_lists.DatamailerRecipientListSendClient.send_to_transient_list" ) diff --git a/courses/tests/test_deadline_reminder_failures.py b/courses/tests/test_deadline_reminder_failures.py index f677a509..9e781cce 100644 --- a/courses/tests/test_deadline_reminder_failures.py +++ b/courses/tests/test_deadline_reminder_failures.py @@ -8,7 +8,7 @@ from courses.models import Homework from courses.tests.deadline_reminder_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DeadlineReminderTestBase, ) from data.models import DatamailerSendAudit, DatamailerSendAuditStatus @@ -42,7 +42,7 @@ def create_two_homeworks(self, course, now): ) return first, second - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch(SEND_TARGET) def test_second_reminder_is_sent_when_first_one_fails(self, send_transient): now = self.reminder_run_time() @@ -64,7 +64,7 @@ def test_second_reminder_is_sent_when_first_one_fails(self, send_transient): # Both events must have been attempted, not just the first. self.assertEqual(send_transient.call_count, 2) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch(SEND_TARGET) def test_failure_is_reported_on_stderr_with_reason(self, send_transient): now = self.reminder_run_time() @@ -89,7 +89,7 @@ def test_failure_is_reported_on_stderr_with_reason(self, send_transient): self.assertIn("homework-1", error_output) self.assertIn("timed out", error_output) - @override_settings(**DATAMAILER_SETTINGS) + @override_settings(**RELAY_SETTINGS) @patch(SEND_TARGET) def test_failed_send_records_error_on_audit(self, send_transient): now = self.reminder_run_time() diff --git a/courses/tests/test_deadline_reminder_homework.py b/courses/tests/test_deadline_reminder_homework.py index b71d9122..bf00f5e8 100644 --- a/courses/tests/test_deadline_reminder_homework.py +++ b/courses/tests/test_deadline_reminder_homework.py @@ -4,7 +4,7 @@ from django.test import override_settings from courses.tests.deadline_reminder_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DeadlineReminderTestBase, ) from courses.tests.deadline_reminder_homework import ( @@ -16,7 +16,7 @@ class HomeworkDeadlineReminderCommandTest(DeadlineReminderTestBase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( diff --git a/courses/tests/test_deadline_reminder_peer_review.py b/courses/tests/test_deadline_reminder_peer_review.py index 47ff3a92..d52271d5 100644 --- a/courses/tests/test_deadline_reminder_peer_review.py +++ b/courses/tests/test_deadline_reminder_peer_review.py @@ -3,7 +3,7 @@ from django.test import override_settings from courses.tests.deadline_reminder_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DeadlineReminderTestBase, ) from courses.tests.deadline_reminder_peer_review import ( @@ -14,7 +14,7 @@ class PeerReviewDeadlineReminderCommandTest(DeadlineReminderTestBase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( diff --git a/courses/tests/test_deadline_reminder_project.py b/courses/tests/test_deadline_reminder_project.py index 8d97b258..7dc15bab 100644 --- a/courses/tests/test_deadline_reminder_project.py +++ b/courses/tests/test_deadline_reminder_project.py @@ -3,7 +3,7 @@ from django.test import override_settings from courses.tests.deadline_reminder_base import ( - DATAMAILER_SETTINGS, + RELAY_SETTINGS, DeadlineReminderTestBase, ) from courses.tests.deadline_reminder_project import ( @@ -14,7 +14,7 @@ class ProjectSubmissionDeadlineReminderCommandTest(DeadlineReminderTestBase): @override_settings( - **DATAMAILER_SETTINGS, + **RELAY_SETTINGS, PUBLIC_BASE_URL="https://courses.example.com", ) @patch( diff --git a/courses/tests/test_registration_campaign_notifications.py b/courses/tests/test_registration_campaign_notifications.py index 5a9a7d02..7584b696 100644 --- a/courses/tests/test_registration_campaign_notifications.py +++ b/courses/tests/test_registration_campaign_notifications.py @@ -8,10 +8,10 @@ class RegistrationCampaignNotificationTests(RegistrationCampaignBase): @override_settings( - DATAMAILER_URL="https://datamailer.example.com", - DATAMAILER_API_KEY="secret-token", - DATAMAILER_CLIENT="dtc-courses", - DATAMAILER_AUDIENCE="dtc-courses", + RELAY_URL="https://relay.example.com", + RELAY_API_KEY="secret-token", + RELAY_CLIENT="dtc-courses", + RELAY_AUDIENCE="dtc-courses", ) @patch( "courses.views.registration.send_registration_confirmation_email" diff --git a/courses/tests/test_registration_campaigns.py b/courses/tests/test_registration_campaigns.py index e2f15ab3..5241cb45 100644 --- a/courses/tests/test_registration_campaigns.py +++ b/courses/tests/test_registration_campaigns.py @@ -77,10 +77,10 @@ def test_registration_page_hides_count_when_no_signups(self): self.assertNotContains(response, "already registered") @override_settings( - DATAMAILER_URL="", - DATAMAILER_API_KEY="", - DATAMAILER_CLIENT="", - DATAMAILER_AUDIENCE="", + RELAY_URL="", + RELAY_API_KEY="", + RELAY_CLIENT="", + RELAY_AUDIENCE="", ) def test_anonymous_registration_creates_independent_registration( self, @@ -103,10 +103,10 @@ def test_anonymous_registration_creates_independent_registration( self.assertIsNone(registration.user) @override_settings( - DATAMAILER_URL="", - DATAMAILER_API_KEY="", - DATAMAILER_CLIENT="", - DATAMAILER_AUDIENCE="", + RELAY_URL="", + RELAY_API_KEY="", + RELAY_CLIENT="", + RELAY_AUDIENCE="", ) def test_registration_stores_optional_company_name(self): url = self.campaign_url() @@ -144,10 +144,10 @@ def test_duplicate_registration_shows_message(self): self.assertEqual(registration_count, 1) @override_settings( - DATAMAILER_URL="", - DATAMAILER_API_KEY="", - DATAMAILER_CLIENT="", - DATAMAILER_AUDIENCE="", + RELAY_URL="", + RELAY_API_KEY="", + RELAY_CLIENT="", + RELAY_AUDIENCE="", ) def test_registration_requires_only_email_and_newsletter_consent(self): url = self.campaign_url() @@ -218,10 +218,10 @@ def test_anonymous_registration_page_does_not_show_logout_link(self): self.assertNotContains(response, "to use a different email address") @override_settings( - DATAMAILER_URL="", - DATAMAILER_API_KEY="", - DATAMAILER_CLIENT="", - DATAMAILER_AUDIENCE="", + RELAY_URL="", + RELAY_API_KEY="", + RELAY_CLIENT="", + RELAY_AUDIENCE="", ) def test_logged_in_registration_blank_optional_fields_keeps_profile(self): user = self.create_signed_blank_user() diff --git a/data/tests/test_datamailer_callback_status.py b/data/tests/test_datamailer_callback_status.py index 8be7b7cc..01d4fbef 100644 --- a/data/tests/test_datamailer_callback_status.py +++ b/data/tests/test_datamailer_callback_status.py @@ -7,7 +7,7 @@ class DatamailerCallbackStatusCommandTest(DatamailerWebhookTestBase): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_callback_status_command_reports_counts_and_duplicates(self): failed_payload = { "event_id": "evt-1", diff --git a/data/tests/test_datamailer_webhook.py b/data/tests/test_datamailer_webhook.py index 560981e7..191fc9ea 100644 --- a/data/tests/test_datamailer_webhook.py +++ b/data/tests/test_datamailer_webhook.py @@ -17,7 +17,7 @@ def test_webhook_requires_configured_token(self): self.assertEqual(response.status_code, 503) - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_rejects_invalid_token(self): payload = { "event_id": "evt-1", @@ -31,7 +31,7 @@ def test_webhook_rejects_invalid_token(self): class DatamailerWebhookContactTest(DatamailerWebhookTestBase): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_records_contact_event_idempotently(self): payload = { "event_id": "evt-1", diff --git a/data/tests/test_datamailer_webhook_events.py b/data/tests/test_datamailer_webhook_events.py index 87bba40d..78813132 100644 --- a/data/tests/test_datamailer_webhook_events.py +++ b/data/tests/test_datamailer_webhook_events.py @@ -6,7 +6,7 @@ class DatamailerWebhookTransactionalEventTest(DatamailerWebhookTestBase): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_records_transactional_failure_event(self): payload = { "event_id": "evt-tx-failed-1", @@ -30,7 +30,7 @@ def test_webhook_records_transactional_failure_event(self): 123, ) - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_records_transactional_skipped_event(self): payload = { "event_id": "evt-tx-skipped-1", @@ -47,7 +47,7 @@ def test_webhook_records_transactional_skipped_event(self): class DatamailerWebhookMessageEventTest(DatamailerWebhookTestBase): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_records_message_lifecycle_event(self): payload = { "event_id": "evt-message-clicked-1", diff --git a/data/tests/test_datamailer_webhook_preferences.py b/data/tests/test_datamailer_webhook_preferences.py index 2eb46aaa..b59e1acc 100644 --- a/data/tests/test_datamailer_webhook_preferences.py +++ b/data/tests/test_datamailer_webhook_preferences.py @@ -8,7 +8,7 @@ class DatamailerWebhookUnsubscribePreferenceTest( DatamailerWebhookTestBase, ): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_unsubscribe_records_known_preference(self): user = self.create_student_user() payload = { @@ -28,7 +28,7 @@ def test_webhook_unsubscribe_records_known_preference(self): event = DatamailerContactEvent.objects.get() self.assertEqual(event.preference_key, "email_deadline_reminders") - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_unsubscribe_records_preference_from_metadata(self): user = self.create_student_user() metadata = { @@ -53,7 +53,7 @@ def test_webhook_unsubscribe_records_preference_from_metadata(self): class DatamailerWebhookUnsubscribeEventTest(DatamailerWebhookTestBase): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_unsubscribe_without_preference_only_records_event(self): user = self.create_student_user() payload = { @@ -74,7 +74,7 @@ def test_webhook_unsubscribe_without_preference_only_records_event(self): class DatamailerWebhookResubscribeTest(DatamailerWebhookTestBase): - @override_settings(DATAMAILER_WEBHOOK_TOKEN="secret-token") + @override_settings(RELAY_WEBHOOK_TOKEN="secret-token") def test_webhook_records_resubscribe_without_preference_change(self): user = self.create_student_user() payload = { diff --git a/docs/datamailer-integration.md b/docs/datamailer-integration.md index 1c795601..c5016561 100644 --- a/docs/datamailer-integration.md +++ b/docs/datamailer-integration.md @@ -1159,7 +1159,7 @@ as an operational safety net, but do not put reconcile back into every send. panel (§5) assumes a contact already exists with sane defaults. Target rules: - **Contact creation:** CMP creates/upserts a Datamailer contact on course - registration, account creation when `DATAMAILER_SYNC_ON_USER_CREATE=1`, and the + registration, account creation when `RELAY_SYNC_ON_USER_CREATE=1`, and the first enrollment/submission event if the contact does not already exist. - **Consent:** course registration remains the explicit newsletter / course email consent point. CMP sends `status: subscribed`, `verified: true`, and @@ -1314,22 +1314,22 @@ Planned, not implemented yet: CMP enables Datamailer only when all required settings are present: ```text -DATAMAILER_URL -DATAMAILER_API_KEY -DATAMAILER_CLIENT -DATAMAILER_AUDIENCE +RELAY_URL +RELAY_API_KEY +RELAY_CLIENT +RELAY_AUDIENCE ``` CMP may also set: ```text -DATAMAILER_FROM_EMAIL -DATAMAILER_STRICT -DATAMAILER_WEBHOOK_TOKEN -DATAMAILER_SYNC_ON_USER_CREATE +RELAY_FROM_EMAIL +RELAY_STRICT +RELAY_WEBHOOK_TOKEN +RELAY_SYNC_ON_USER_CREATE ``` -`DATAMAILER_FROM_EMAIL` is a Datamailer sender ID, not a raw email address. +`RELAY_FROM_EMAIL` is a Datamailer sender ID, not a raw email address. Datamailer validates it against the authenticated client sender configuration. CMP keeps transactional template keys as code-level constants. We don't add one @@ -1338,8 +1338,8 @@ environment variable per template. `PUBLIC_BASE_URL` is a CMP URL-building setting, not a Datamailer API setting. CMP uses it when it builds links for email context. -When `DATAMAILER_STRICT=0`, CMP logs Datamailer failures and lets the course -flow continue. When `DATAMAILER_STRICT=1`, CMP raises the Datamailer API failure +When `RELAY_STRICT=0`, CMP logs Datamailer failures and lets the course +flow continue. When `RELAY_STRICT=1`, CMP raises the Datamailer API failure to the caller. ### Where configuration lives — two sides @@ -1353,7 +1353,7 @@ have **exactly one home**: address, the subscription **category tags** (§5), and — a strong candidate — the **link base host** (so templates compose `{{ base_url }}/{path}` and CMP never repeats it). Pinned here, an empty host is impossible. -- **CMP-side — settings + per-send context.** Connection config (`DATAMAILER_URL`, +- **CMP-side — settings + per-send context.** Connection config (`RELAY_URL`, `_API_KEY`, `_CLIENT`, `_AUDIENCE`), and the **per-message context** CMP computes: course title, slugs, scores, paths. If the base host is *not* a Datamailer client-global, then CMP pins `PUBLIC_BASE_URL` and sends fully-qualified URLs. @@ -1498,7 +1498,7 @@ it upserts registrants into Datamailer. ```text POST /api/contacts -Authorization: Bearer +Authorization: Bearer ``` ```json @@ -1544,7 +1544,7 @@ Single-recipient sends (submission confirmations, certificates): ```text POST /api/transactional/send -Authorization: Bearer +Authorization: Bearer ``` ```json @@ -1581,7 +1581,7 @@ current computed audience. The send then names the node or transient list: ```text POST /api/recipient-lists/{list_key}/transactional-send -Authorization: Bearer +Authorization: Bearer ``` ```json @@ -1667,7 +1667,7 @@ Rules: CMP verifies the transactional email path without delivering anything by using Datamailer's `dry_run` flag on `POST /api/transactional/send`. When -`DATAMAILER_TRANSACTIONAL_DRY_RUN=1`, CMP adds `"dry_run": true` to every +`RELAY_TRANSACTIONAL_DRY_RUN=1`, CMP adds `"dry_run": true` to every transactional send. Datamailer runs the **identical** validate/render pipeline (template lookup, context merge, required-field validation, category and suppression checks, render) and returns the rendered email inline **without** @@ -1699,9 +1699,9 @@ curl -s -H "Authorization: Token $TOKEN" \ ``` For a local or CI end-to-end run, point CMP at a Datamailer deployment with -normal settings (`DATAMAILER_URL`, `DATAMAILER_API_KEY`, `DATAMAILER_CLIENT`, -`DATAMAILER_AUDIENCE`, `DATAMAILER_FROM_EMAIL`) and set -`DATAMAILER_TRANSACTIONAL_DRY_RUN=1`. Use CMP normally (register, submit +normal settings (`RELAY_URL`, `RELAY_API_KEY`, `RELAY_CLIENT`, +`RELAY_AUDIENCE`, `RELAY_FROM_EMAIL`) and set +`RELAY_TRANSACTIONAL_DRY_RUN=1`. Use CMP normally (register, submit homework, publish scores, and so on) and inspect the rendered results through `GET /api/datamailer/send-audits`. @@ -1841,7 +1841,7 @@ For large lists, CMP can use Datamailer's file-by-reference import jobs instead of sending members inline: ```console -DATAMAILER_IMPORT_S3_BUCKET=cmp-datamailer-imports \ +RELAY_IMPORT_S3_BUCKET=cmp-datamailer-imports \ uv run python manage.py sync_datamailer_recipient_lists registrations \ --course-slug ml-zoomcamp-2026 \ --import-by-reference \ @@ -1980,7 +1980,7 @@ preferences (conceptually §5): ```text POST /api/datamailer/events -Authorization: Bearer +Authorization: Bearer ``` CMP stores each callback in `data_datamailercontactevent` keyed by Datamailer's @@ -2089,7 +2089,7 @@ Target tests before relying on the integration: category/global-unsubscribe suppression decision, and link diagnostics for the same request, while the dry run never enqueues provider delivery. - **Local E2E dry-run tests:** run CMP against a Datamailer deployment with - `DATAMAILER_TRANSACTIONAL_DRY_RUN=1`. Use CMP normally to register, submit, + `RELAY_TRANSACTIONAL_DRY_RUN=1`. Use CMP normally to register, submit, publish scores, assign peer reviews, send deadline reminders, and publish certificates. Assert on the rendered text/HTML, suppression decisions, and links read back from `GET /api/datamailer/send-audits`. diff --git a/e2e/README.md b/e2e/README.md index cf3e62f2..1c4043d4 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -31,7 +31,7 @@ talks to the remote server over HTTP and a browser. | 3. Enrollment & identity (create/find student, impersonate, profile) | `tests/test_02_enrollment.py` | browser (loginas) | | 4. Homework flow (submit via UI, confirmation, score, leaderboard) | `tests/test_03_homework.py` | browser + API | | 5. Project flow (submit via UI, assign reviews, score, stats) | `tests/test_04_project.py` | browser + API | -| 6. Email verification (homework + project confirmation emails) | `tests/test_03/04` (`@pytest.mark.email`) + `tests/test_06` (client unit tests) | CMP send audit (Datamailer dry-run render) | +| 6. Email verification (homework + project confirmation emails) | `tests/test_03/04` (`@pytest.mark.email`) + `tests/test_06` (client unit tests) | CMP send audit (Relay dry-run render) | | 7. Dashboards & stats render | `tests/test_05_dashboards.py` | browser | | 8. Teardown + pre-run sweep + clean assert | `tests/test_99_teardown.py` | browser + API | @@ -40,8 +40,8 @@ talks to the remote server over HTTP and a browser. Email checks mimic the production path but deliver nothing. CMP's real outbox → dispatch → `POST /api/transactional/send` → `DatamailerSendAudit` pipeline runs exactly as in prod, except the target deployment sets -`DATAMAILER_TRANSACTIONAL_DRY_RUN=1`. CMP then adds Datamailer's `dry_run` flag -to every transactional send: Datamailer runs the identical validate/render +`RELAY_TRANSACTIONAL_DRY_RUN=1`. CMP then adds Relay's `dry_run` flag +to every transactional send: Relay runs the identical validate/render pipeline and returns the rendered email inline **without** sending, queuing, or persisting anything. @@ -64,8 +64,8 @@ context) contains `/homework/` / `/project/`. **What runs now vs. what's gated.** The client logic is covered by `tests/test_06_send_audit_client.py` (no-network unit tests — path, query params, response shape, poll/timeout, body matching). The **live** email -assertions in `test_03/04` need the target deployment to have Datamailer -configured **and** `DATAMAILER_TRANSACTIONAL_DRY_RUN=1`; when no matching audit +assertions in `test_03/04` need the target deployment to have Relay +configured **and** `RELAY_TRANSACTIONAL_DRY_RUN=1`; when no matching audit appears, they **xfail** (a fast pre-check avoids burning the poll timeout) so the suite stays green until dry-run is switched on. diff --git a/e2e/api_client.py b/e2e/api_client.py index b3d3d3b0..e5d0bd8d 100644 --- a/e2e/api_client.py +++ b/e2e/api_client.py @@ -355,7 +355,7 @@ def datamailer_send_audits( Mirrors the production email path: every send goes outbox -> dispatch -> ``/api/transactional/send`` -> ``DatamailerSendAudit``. With - ``DATAMAILER_TRANSACTIONAL_DRY_RUN=1`` on the target, the audit's + ``RELAY_TRANSACTIONAL_DRY_RUN=1`` on the target, the audit's ``response_payload`` carries the rendered subject/bodies without anything being delivered. """ @@ -415,7 +415,7 @@ def wait_for_send_audit( f"template_key={template_key!r} / body~={body_contains!r} within " f"{timeout}s. Seen: {[a.get('idempotency_key') for a in seen]!r}. " "Is Datamailer configured on the target with " - "DATAMAILER_TRANSACTIONAL_DRY_RUN=1?" + "RELAY_TRANSACTIONAL_DRY_RUN=1?" ) diff --git a/e2e/conftest.py b/e2e/conftest.py index d2b87b39..4f78113d 100644 --- a/e2e/conftest.py +++ b/e2e/conftest.py @@ -67,7 +67,7 @@ def send_audits(api: CmpApiClient) -> CmpApiClient: Email verification no longer polls a mock/real inbox. Instead it reads CMP's ``DatamailerSendAudit`` rows over HTTP: the real prod path runs (outbox -> dispatch -> /api/transactional/send -> audit), but with - ``DATAMAILER_TRANSACTIONAL_DRY_RUN=1`` the render is returned inline and + ``RELAY_TRANSACTIONAL_DRY_RUN=1`` the render is returned inline and nothing is delivered. This fixture is the same ``CmpApiClient`` used for provisioning; it exposes ``datamailer_send_audits`` / ``wait_for_send_audit``. """ diff --git a/e2e/tests/test_03_homework.py b/e2e/tests/test_03_homework.py index 47ab9848..c1366e90 100644 --- a/e2e/tests/test_03_homework.py +++ b/e2e/tests/test_03_homework.py @@ -154,7 +154,7 @@ def test_homework_confirmation_email(send_audits, run_state): Verification reads CMP's own ``DatamailerSendAudit`` over HTTP rather than an inbox: the prod path runs (outbox -> dispatch -> /api/transactional/send - -> audit), but with ``DATAMAILER_TRANSACTIONAL_DRY_RUN=1`` the render is + -> audit), but with ``RELAY_TRANSACTIONAL_DRY_RUN=1`` the render is returned inline and nothing is delivered. xfails cleanly when no audit appears (Datamailer not configured / dry-run off on the target), so the suite stays green until it is switched on. @@ -170,7 +170,7 @@ def test_homework_confirmation_email(send_audits, run_state): except SendAuditTimeout as exc: pytest.xfail( "No homework-confirmation send audit found; ensure Datamailer is " - "configured on the target with DATAMAILER_TRANSACTIONAL_DRY_RUN=1. " + "configured on the target with RELAY_TRANSACTIONAL_DRY_RUN=1. " f"({exc})" ) assert audit["template_key"] == HOMEWORK_CONFIRMATION_TEMPLATE diff --git a/e2e/tests/test_04_project.py b/e2e/tests/test_04_project.py index 868dd3c5..5c1c3b48 100644 --- a/e2e/tests/test_04_project.py +++ b/e2e/tests/test_04_project.py @@ -76,7 +76,7 @@ def test_project_confirmation_email(send_audits, run_state): Verification reads CMP's own ``DatamailerSendAudit`` over HTTP: the prod path runs (outbox -> dispatch -> /api/transactional/send -> audit) with - ``DATAMAILER_TRANSACTIONAL_DRY_RUN=1``, so the render is returned inline and + ``RELAY_TRANSACTIONAL_DRY_RUN=1``, so the render is returned inline and nothing is delivered. xfails cleanly when no audit appears. """ require_project(run_state) @@ -90,7 +90,7 @@ def test_project_confirmation_email(send_audits, run_state): except SendAuditTimeout as exc: pytest.xfail( "No project-confirmation send audit found; ensure Datamailer is " - "configured on the target with DATAMAILER_TRANSACTIONAL_DRY_RUN=1. " + "configured on the target with RELAY_TRANSACTIONAL_DRY_RUN=1. " f"({exc})" ) assert audit["template_key"] == PROJECT_CONFIRMATION_TEMPLATE diff --git a/e2e/tests/test_06_send_audit_client.py b/e2e/tests/test_06_send_audit_client.py index 5eb89d6b..6b62854e 100644 --- a/e2e/tests/test_06_send_audit_client.py +++ b/e2e/tests/test_06_send_audit_client.py @@ -6,7 +6,7 @@ targets (path, query params, auth header, response shape, poll/timeout, and the rendered-body matching used by the email tests). The *live* email assertions (``test_03/04``) still need a deployed dev target running with -``DATAMAILER_TRANSACTIONAL_DRY_RUN=1``. +``RELAY_TRANSACTIONAL_DRY_RUN=1``. """ import json