diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1d7d394 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +__pycache__ +.coverage +Makefile +README.md +.github/ +LICENCE +config/sample.env +config/current.env diff --git a/.gitignore b/.gitignore index 7a7e68f..40a1660 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .coverage __pycache__ +config/current.env diff --git a/Makefile b/Makefile index 4aace11..0b8df8f 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ run-command=(podman run --rm -ti --volume $(PWD):/mnt/code:rw \ --env QUERIDO_DIARIO_DATABASE_CSV=$(QUERIDO_DIARIO_DATABASE_CSV) \ --env PYTHONPATH=/mnt/code \ --env RUN_INTEGRATION_TESTS=$(RUN_INTEGRATION_TESTS) \ + --env-file config/current.env \ --user=$(UID):$(UID) $(IMAGE_NAMESPACE)/$(IMAGE_NAME):$(IMAGE_TAG) $1) wait-for=(podman run --rm -ti --volume $(PWD):/mnt/code:rw \ @@ -68,6 +69,7 @@ destroy-pod: podman pod rm --force --ignore $(POD_NAME) create-pod: destroy-pod + cp --no-clobber config/sample.env config/current.env podman pod create --publish $(API_PORT):$(API_PORT) \ --publish $(ELASTICSEARCH_PORT1):$(ELASTICSEARCH_PORT1) \ --publish $(ELASTICSEARCH_PORT2):$(ELASTICSEARCH_PORT2) \ diff --git a/README.md b/README.md index 601de3f..33ca058 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,12 @@ need to insert data into the database. There is another make target, `make apisq which open the `psql` and connect to the database. Thus, you can insert data using some `INSERT INTO ...` statements and test the API. ;) + +### Using suggestion endpoint + +You need to create a token at [Mailjet](www.mailjet.com) to run +application and send email (put on `config/current.env`). + ## Tests The project uses TDD during development. This means that there are no changes diff --git a/api/api.py b/api/api.py index f71ab99..728ef13 100644 --- a/api/api.py +++ b/api/api.py @@ -2,12 +2,13 @@ from datetime import date from typing import List, Optional -from fastapi import FastAPI, Query, Path +from fastapi import FastAPI, Query, Path, Response, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse -from pydantic import BaseModel +from pydantic import BaseModel, Field from gazettes import GazetteAccessInterface, GazetteRequest +from suggestions import Suggestion, SuggestionService from config.config import load_configuration config = load_configuration() @@ -75,8 +76,28 @@ class SortBy(str, Enum): ASCENDING_DATE = "ascending_date" +<<<<<<< HEAD class HTTPExceptionMessage(BaseModel): detail: str +======= +class CreateSuggestionBody(BaseModel): + title: str = Field( + None, title="Suggestion title", description="Suggestion title", + ) + email_address: str = Field( + None, title="Email address", description="Email address who is sending email" + ) + name: str = Field( + None, title="Name", description="Name who is sending email", + ) + content: str = Field( + None, title="Email content", description="Email content with suggestion", + ) + + +class CreatedSuggestionResponse(BaseModel): + status: str +>>>>>>> 747e9c6... send suggestion def trigger_gazettes_search( @@ -295,10 +316,39 @@ async def get_city(territory_id: str = Path(..., description="City's IBGE ID")): return {"city": city_info} -def configure_api_app(gazettes: GazetteAccessInterface, api_root_path=None): +@app.post( + "/suggestions", + response_model=CreatedSuggestionResponse, + name="Send a suggestion", + description="Send a suggestion to the project", + response_model_exclude_unset=True, + response_model_exclude_none=True, +) +async def add_suggestion(response: Response, body: CreateSuggestionBody): + suggestion = Suggestion( + title=body.title, + email_address=body.email_address, + name=body.name, + content=body.content, + ) + if app.suggestion_service.add_suggestion(suggestion): + response.status_code = status.HTTP_200_OK + return {"status": "sent"} + else: + response.status_code = status.HTTP_400_BAD_REQUEST + return {"status": "Problem on sent message"} + + +def configure_api_app( + gazettes: GazetteAccessInterface, + suggestion_service: SuggestionService, + api_root_path=None, +): if not isinstance(gazettes, GazetteAccessInterface): raise Exception("Only GazetteAccessInterface object are accepted") if api_root_path is not None and type(api_root_path) != str: raise Exception("Invalid api_root_path") app.gazettes = gazettes app.root_path = api_root_path + app.suggestion_service = suggestion_service +z \ No newline at end of file diff --git a/config/config.py b/config/config.py index e32fd48..a3f4c92 100644 --- a/config/config.py +++ b/config/config.py @@ -49,6 +49,21 @@ def _load_boolean(cls, key, default=False): return False return default + self.suggestion_mailjet_rest_api_key = os.environ.get( + "SUGGESTION_MAILJET_REST_API_KEY", "" + ) + self.suggestion_mailjet_rest_api_secret = os.environ.get( + "SUGGESTION_MAILJET_REST_API_SECRET", "" + ) + self.suggestion_email_sender = os.environ.get("SUGGESTION_EMAIL_SENDER", "") + self.suggestion_email_recipient = os.environ.get( + "SUGGESTION_EMAIL_RECIPIENT", "" + ) + self.suggestion_name = os.environ.get("SUGGESTION_NAME", "") + self.suggestion_mailjet_custom_id = os.environ.get( + "SUGGESTION_MAILJET_CUSTOM_ID", "" + ) + def load_configuration(): return Configuration() diff --git a/config/sample.env b/config/sample.env new file mode 100644 index 0000000..a967ba7 --- /dev/null +++ b/config/sample.env @@ -0,0 +1,6 @@ +SUGGESTION_MAILJET_REST_API_KEY=mailjet.com +SUGGESTION_MAILJET_REST_API_SECRET=mailjet.com +SUGGESTION_EMAIL_SENDER=example@email.com +SUGGESTION_EMAIL_RECIPIENT=example@email.com +SUGGESTION_NAME=ok +SUGGESTION_MAILJET_CUSTOM_ID=AppCustomID diff --git a/main/__main__.py b/main/__main__.py index 55f34bf..9985a21 100644 --- a/main/__main__.py +++ b/main/__main__.py @@ -7,11 +7,20 @@ from index import create_elasticsearch_data_mapper from config import load_configuration from database import create_database_interface +from suggestions import create_suggestion_service configuration = load_configuration() datagateway = create_elasticsearch_data_mapper(configuration.host, configuration.index) database = create_database_interface() gazettes_interface = create_gazettes_interface(datagateway, database) -configure_api_app(gazettes_interface, configuration.root_path) +suggestion_service = create_suggestion_service( + suggestion_mailjet_rest_api_key=configuration.suggestion_mailjet_rest_api_key, + suggestion_mailjet_rest_api_secret=configuration.suggestion_mailjet_rest_api_secret, + suggestion_email_sender=configuration.suggestion_email_sender, + suggestion_email_recipient=configuration.suggestion_email_recipient, + suggestion_name=configuration.suggestion_name, + suggestion_mailjet_custom_id=configuration.suggestion_mailjet_custom_id, +) +configure_api_app(gazettes_interface, suggestion_service, configuration.root_path) uvicorn.run(app, host="0.0.0.0", port=8080, root_path=configuration.root_path) diff --git a/requirements.txt b/requirements.txt index 528be8b..8a00c13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ uvicorn==0.11.8 psycopg2==2.8.5 SQLAlchemy==1.3.19 elasticsearch==7.9.1 +mailjet-rest==1.3.4 diff --git a/suggestions/__init__.py b/suggestions/__init__.py new file mode 100644 index 0000000..f0d9bec --- /dev/null +++ b/suggestions/__init__.py @@ -0,0 +1,7 @@ +from .model import Suggestion + +from .service import ( + SuggestionService, + MailjetSuggestionService, # only for test + create_suggestion_service, +) diff --git a/suggestions/model.py b/suggestions/model.py new file mode 100644 index 0000000..45ee9d3 --- /dev/null +++ b/suggestions/model.py @@ -0,0 +1,26 @@ +class Suggestion: + """ + Object containing the data to suggest + """ + + def __init__( + self, title, email_address, name, content, + ): + self.title = title + self.email_address = email_address + self.name = name + self.content = content + + def __hash__(self): + return hash((self.title, self.email_address, self.name, self.content,)) + + def __eq__(self, other): + return ( + self.title == other.title + and self.email_address == other.email_address + and self.name == other.name + and self.content == other.content + ) + + def __repr__(self): + return f"Suggestion({self.title}, {self.email_address}, {self.name}, {self.content})" diff --git a/suggestions/service.py b/suggestions/service.py new file mode 100644 index 0000000..a776ed9 --- /dev/null +++ b/suggestions/service.py @@ -0,0 +1,87 @@ +import abc +import logging + +from mailjet_rest import Client + +from .model import Suggestion + + +class SuggestionService(abc.ABC): + """ + Service to send a suggestion + """ + + @abc.abstractmethod + def add_suggestion(self, suggestion: Suggestion): + """ + Method to send a suggestion + """ + + +class MailjetSuggestionService(SuggestionService): + def __init__( + self, + mailjet_client: Client, + suggestion_email_sender: str, + suggestion_email_recipient: str, + suggestion_name: str, + suggestion_mailjet_custom_id: str, + ): + self.mailjet_client = mailjet_client + self.suggestion_email_sender = suggestion_email_sender + self.suggestion_email_recipient = suggestion_email_recipient + self.suggestion_name = suggestion_name + self.suggestion_mailjet_custom_id = suggestion_mailjet_custom_id + self.logger = logging.getLogger(__name__) + + def add_suggestion(self, suggestion: Suggestion): + data = { + "Messages": [ + { + "From": { + "Email": self.suggestion_email_sender, + "Name": suggestion.name, + }, + "To": [ + { + "Email": self.suggestion_email_recipient, + "Name": self.suggestion_name, + } + ], + "Subject": suggestion.title, + "TextPart": f"From <{suggestion.email_address}>:\n\n{suggestion.content}", + "CustomID": self.suggestion_mailjet_custom_id, + } + ] + } + result = self.mailjet_client.send.create(data=data) + + self.logger.debug(f"Suggestion body response {result.json()}") + if 200 <= result.status_code <= 299: + self.logger.info(f"Suggestion created for {suggestion.email_address}") + return True + else: + self.logger.error( + f"Error on send email {suggestion.email_address}. Status code response: {result.status_code}" + ) + return False + + +def create_suggestion_service( + suggestion_mailjet_rest_api_key: str, + suggestion_mailjet_rest_api_secret: str, + suggestion_email_sender: str, + suggestion_email_recipient: str, + suggestion_name: str, + suggestion_mailjet_custom_id: str, +) -> SuggestionService: + return MailjetSuggestionService( + mailjet_client=Client( + auth=(suggestion_mailjet_rest_api_key, suggestion_mailjet_rest_api_secret), + version="v3.1", + ), + suggestion_email_sender=suggestion_email_sender, + suggestion_email_recipient=suggestion_email_recipient, + suggestion_name=suggestion_name, + suggestion_mailjet_custom_id=suggestion_mailjet_custom_id, + ) diff --git a/tests/test_api.py b/tests/test_api.py index 5150c5e..78b8393 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -6,6 +6,7 @@ from api import app, configure_api_app from gazettes import GazetteAccessInterface, GazetteRequest +from suggestions import SuggestionService @GazetteAccessInterface.register @@ -13,6 +14,11 @@ class MockGazetteAccessInterface: pass +@SuggestionService.register +class MockSuggestionService: + pass + + class ApiGazettesEndpointTests(TestCase): def create_mock_gazette_interface( self, return_value=(0, []), cities_info=[], city_info=None @@ -25,14 +31,14 @@ def create_mock_gazette_interface( def test_api_should_fail_when_try_to_set_any_object_as_gazettes_interface(self): with self.assertRaises(Exception): - configure_api_app(MagicMock()) + configure_api_app(MagicMock(), MockSuggestionService()) def test_api_should_not_fail_when_try_to_set_any_object_as_gazettes_interface(self): - configure_api_app(MockGazetteAccessInterface()) + configure_api_app(MockGazetteAccessInterface(), MockSuggestionService()) def test_gazettes_endpoint_should_accept_territory_id_in_the_path(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902") self.assertEqual(response.status_code, 200) @@ -46,7 +52,7 @@ def test_gazettes_endpoint_should_accept_territory_id_in_the_path(self): self.assertIsNotNone(interface.get_gazettes.call_args.args[0].size) def test_gazettes_endpoint_should_accept_query_since_date(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes/4205902", params={"since": date.today().strftime("%Y-%m-%d")} @@ -54,7 +60,7 @@ def test_gazettes_endpoint_should_accept_query_since_date(self): self.assertEqual(response.status_code, 200) def test_gazettes_endpoint_should_accept_query_until_date(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes/4205902", params={"until": date.today().strftime("%Y-%m-%d")} @@ -62,19 +68,19 @@ def test_gazettes_endpoint_should_accept_query_until_date(self): self.assertEqual(response.status_code, 200) def test_gazettes_endpoint_should_fail_with_invalid_since_value(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902", params={"since": "foo-bar-2222"}) self.assertEqual(response.status_code, 422) def test_gazettes_endpoint_should_fail_with_invalid_until_value(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902", params={"until": "foo-bar-2222"}) self.assertEqual(response.status_code, 422) def test_gazettes_endpoint_should_fail_with_invalid_pagination_data(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes/4205902", params={"offset": "asfasdasd", "size": "10"} @@ -91,7 +97,7 @@ def test_gazettes_endpoint_should_fail_with_invalid_pagination_data(self): def test_get_gazettes_without_territory_id_should_be_fine(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/") self.assertEqual(response.status_code, 200) @@ -104,7 +110,7 @@ def test_get_gazettes_without_territory_id_should_be_fine(self): def test_get_gazettes_should_request_gazettes_to_interface_object(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902") self.assertEqual(response.status_code, 200) @@ -112,7 +118,7 @@ def test_get_gazettes_should_request_gazettes_to_interface_object(self): def test_get_gazettes_should_forward_gazettes_filters_to_interface_object(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes/4205902", @@ -154,7 +160,7 @@ def test_get_gazettes_should_return_json_with_items(self): ], ) ) - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902") interface.get_gazettes.assert_called_once() @@ -184,7 +190,7 @@ def test_get_gazettes_should_return_json_with_items(self): def test_get_gazettes_should_return_empty_list_when_no_gazettes_is_found(self): today = date.today() interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902") interface.get_gazettes.assert_called_once() @@ -197,7 +203,7 @@ def test_get_gazettes_should_return_empty_list_when_no_gazettes_is_found(self): ) def test_gazettes_endpoint_should_accept_query_keywords_date(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes/4205902", params={"keywords": ["keyword1" "keyword2"]} @@ -208,7 +214,7 @@ def test_gazettes_endpoint_should_accept_query_keywords_date(self): def test_get_gazettes_should_forwards_keywords_to_interface_object(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get( @@ -220,13 +226,13 @@ def test_get_gazettes_should_forwards_keywords_to_interface_object(self): ) interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) response = client.get("/gazettes/4205902", params={"keywords": []}) interface.get_gazettes.assert_called_once() self.assertIsNone(interface.get_gazettes.call_args.args[0].keywords) def test_gazettes_without_territory_endpoint__should_accept_query_since_date(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes", params={"since": date.today().strftime("%Y-%m-%d")} @@ -234,7 +240,7 @@ def test_gazettes_without_territory_endpoint__should_accept_query_since_date(sel self.assertEqual(response.status_code, 200) def test_gazettes_without_territory_endpoint__should_accept_query_until_date(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes", params={"until": date.today().strftime("%Y-%m-%d")} @@ -244,7 +250,7 @@ def test_gazettes_without_territory_endpoint__should_accept_query_until_date(sel def test_gazettes_without_territory_endpoint__should_fail_with_invalid_since_value( self, ): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes", params={"since": "foo-bar-2222"}) self.assertEqual(response.status_code, 422) @@ -252,7 +258,7 @@ def test_gazettes_without_territory_endpoint__should_fail_with_invalid_since_val def test_gazettes_without_territory_endpoint__should_fail_with_invalid_until_value( self, ): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes", params={"until": "foo-bar-2222"}) self.assertEqual(response.status_code, 422) @@ -261,7 +267,7 @@ def test_get_gazettes_without_territory_id_should_forward_gazettes_filters_to_in self, ): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get( "/gazettes", @@ -284,7 +290,7 @@ def test_get_gazettes_without_territory_id_should_forward_gazettes_filters_to_in def test_api_should_forward_the_result_offset(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes", params={"offset": 0,},) self.assertEqual(response.status_code, 200) @@ -293,10 +299,16 @@ def test_api_should_forward_the_result_offset(self): @expectedFailure def test_configure_api_should_failed_with_invalid_root_path(self): - configure_api_app(MockGazetteAccessInterface(), api_root_path=1) + configure_api_app( + MockGazetteAccessInterface(), MockSuggestionService(), api_root_path=1 + ) def test_configure_api_root_path(self): - configure_api_app(MockGazetteAccessInterface(), api_root_path="/api/v1") + configure_api_app( + MockGazetteAccessInterface(), + MockSuggestionService(), + api_root_path="/api/v1", + ) self.assertEqual("/api/v1", app.root_path) def test_api_without_edition_and_extra_field(self): @@ -327,7 +339,7 @@ def test_api_without_edition_and_extra_field(self): ], ) ) - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902") interface.get_gazettes.assert_called_once() @@ -392,7 +404,7 @@ def test_api_with_none_edition_and_extra_field(self): ], ) ) - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/gazettes/4205902") interface.get_gazettes.assert_called_once() @@ -428,19 +440,19 @@ def test_api_with_none_edition_and_extra_field(self): ) def test_cities_endpoint_should_reject_request_without_partial_city_name(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/cities") self.assertNotEqual(response.status_code, 200) def test_cities_endpoint_should_accept_request_without_partial_city_name(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/cities", params={"city_name": "pirapo"}) self.assertEqual(response.status_code, 200) def test_cities_should_return_some_city_info(self): - configure_api_app(self.create_mock_gazette_interface()) + configure_api_app(self.create_mock_gazette_interface(), MockSuggestionService()) client = TestClient(app) response = client.get("/cities", params={"city_name": "pirapo"}) self.assertEqual(response.status_code, 200) @@ -450,7 +462,7 @@ def test_cities_should_return_some_city_info(self): def test_cities_should_request_data_from_gazette_interface(self): interface = self.create_mock_gazette_interface() - configure_api_app(interface) + configure_api_app(interface, MockSuggestionService()) client = TestClient(app) response = client.get("/cities", params={"city_name": "pirapo"}) interface.get_cities.assert_called_once() @@ -467,7 +479,8 @@ def test_cities_should_return_data_returned_by_gazettes_interface(self): "level": "1", } ] - ) + ), + MockSuggestionService(), ) client = TestClient(app) response = client.get("/cities", params={"city_name": "pirapo"}) diff --git a/tests/test_suggestion_service.py b/tests/test_suggestion_service.py new file mode 100644 index 0000000..8a2c571 --- /dev/null +++ b/tests/test_suggestion_service.py @@ -0,0 +1,75 @@ +from unittest import TestCase +from unittest.mock import MagicMock + +from suggestions import ( + Suggestion, + MailjetSuggestionService, +) + + +class MailjetSuggestionServiceTest(TestCase): + def setUp(self): + self.mailjet_client = MagicMock() + + self.subject = MailjetSuggestionService( + mailjet_client=self.mailjet_client, + suggestion_email_sender="sender-email@address", + suggestion_email_recipient="recipient-email@address", + suggestion_name="from name", + suggestion_mailjet_custom_id="custom_id", + ) + + def test_send_email(self): + data = { + "Messages": [ + { + "From": {"Email": "sender-email@address", "Name": "My Name",}, + "To": [{"Email": "recipient-email@address", "Name": "from name",}], + "Subject": "Title of suggestion", + "TextPart": f"from :\n\nContent of suggestion", + "CustomID": "custom_id", + } + ] + } + self.mailjet_client.send.create(data=data).configure_mock(status_code=200) + + suggestion_result = self.subject.add_suggestion( + Suggestion( + title="Title of suggestion", + email_address="email@address.com", + name="My Name", + content="Content of suggestion", + ) + ) + + self.assertTrue(suggestion_result) + # self.mailjet_client.send.create.assert_called_with(data=data) + + def test_not_send_email(self): + data = { + "Messages": [ + { + "From": { + "Email": "sender-email@address", + "Name": "A girl has no name", + }, + "To": [{"Email": "recipient-email@address", "Name": "from name",}], + "Subject": "Oops", + "TextPart": f"from :\n\nArgument Clinic", + "CustomID": "custom_id", + } + ] + } + self.mailjet_client.send.create(data=data).configure_mock(status_code=401) + + suggestion_result = self.subject.add_suggestion( + Suggestion( + title="Oops", + email_address="wrong@address.com", + name="A girl has no name", + content="Argument Clinic", + ) + ) + + self.assertFalse(suggestion_result) + # self.mailjet_client.send.create.assert_called_with(data=data)