Skip to content

Commit

Permalink
(PC-32840)[API] feat: prevent young users from using ineligible posta…
Browse files Browse the repository at this point in the history
…l codes
  • Loading branch information
dnguyen1-pass committed Jan 7, 2025
1 parent f7ec492 commit fa45a5c
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 11 deletions.
3 changes: 3 additions & 0 deletions api/src/pcapi/core/subscription/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ def complete_profile(
last_name: str,
school_type: users_models.SchoolTypeEnum | None = None,
) -> None:
if postal_code in postal_code_utils.INELIGIBLE_POSTAL_CODES:
raise exceptions.IneligiblePostalCodeException()

update_payload = {
"address": address,
"city": city,
Expand Down
4 changes: 4 additions & 0 deletions api/src/pcapi/core/subscription/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ class SubscriptionException(Exception):
pass


class IneligiblePostalCodeException(SubscriptionException):
pass


class InvalidEligibilityTypeException(SubscriptionException):
pass

Expand Down
7 changes: 7 additions & 0 deletions api/src/pcapi/routes/native/v1/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from pcapi.routes.native.v1.api_errors import account as account_errors
from pcapi.serialization.decorator import spectree_serialize
from pcapi.utils import phone_number as phone_number_utils
from pcapi.utils import postal_code as postal_code_utils

from .. import blueprint
from .serialization import account as serializers
Expand Down Expand Up @@ -69,6 +70,12 @@ def patch_user_profile(
) -> serializers.UserProfileResponse:
profile_update_dict = body.dict(exclude_unset=True)

if (
"postal_code" in profile_update_dict
and profile_update_dict["postal_code"] in postal_code_utils.INELIGIBLE_POSTAL_CODES
):
raise api_errors.ApiErrors({"code": "INELIGIBLE_POSTAL_CODE"})

if "subscriptions" in profile_update_dict:
api.update_notification_subscription(user, body.subscriptions, body.origin)
profile_update_dict.pop("subscriptions", None)
Expand Down
27 changes: 17 additions & 10 deletions api/src/pcapi/routes/native/v1/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pcapi.core.fraud import models as fraud_models
from pcapi.core.fraud.ubble import api as ubble_fraud_api
from pcapi.core.subscription import api as subscription_api
from pcapi.core.subscription import exceptions
from pcapi.core.subscription import models as subscription_models
from pcapi.core.subscription import profile_options
from pcapi.core.subscription.ubble import api as ubble_subscription_api
Expand Down Expand Up @@ -138,16 +139,22 @@ def get_profile(user: users_models.User) -> serializers.ProfileResponse | None:
@spectree_serialize(on_success_status=204, api=blueprint.api)
@authenticated_and_active_user_required
def complete_profile(user: users_models.User, body: serializers.ProfileUpdateRequest) -> None:
subscription_api.complete_profile(
user,
first_name=body.first_name,
last_name=body.last_name,
address=body.address,
city=body.city,
postal_code=body.postal_code,
activity=users_models.ActivityEnum[body.activity_id.value],
school_type=users_models.SchoolTypeEnum[body.school_type_id.value] if body.school_type_id is not None else None,
)
try:
subscription_api.complete_profile(
user,
first_name=body.first_name,
last_name=body.last_name,
address=body.address,
city=body.city,
postal_code=body.postal_code,
activity=users_models.ActivityEnum[body.activity_id.value],
school_type=(
users_models.SchoolTypeEnum[body.school_type_id.value] if body.school_type_id is not None else None
),
)
except exceptions.IneligiblePostalCodeException:
raise api_errors.ApiErrors({"code": "INELIGIBLE_POSTAL_CODE"})

is_activated = subscription_api.activate_beneficiary_if_no_missing_step(user)

if not is_activated:
Expand Down
13 changes: 13 additions & 0 deletions api/tests/routes/native/v1/account_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from pcapi.routes.native.v1.api_errors import account as account_errors
from pcapi.routes.native.v1.serialization import account as account_serializers
from pcapi.utils.date import format_into_utc_date
from pcapi.utils.postal_code import INELIGIBLE_POSTAL_CODES


pytestmark = pytest.mark.usefixtures("db_session")
Expand Down Expand Up @@ -1002,6 +1003,18 @@ def test_postal_code_update(self, client):
assert user.postalCode == "38000"
assert user.city == "Grenoble"

@pytest.mark.parametrize("postal_code", INELIGIBLE_POSTAL_CODES)
def test_ineligible_postal_code_update(self, client, postal_code):
user = users_factories.UserFactory(email=self.identifier)

response = client.with_token(email=self.identifier).patch(
"/native/v1/profile", json={"postalCode": postal_code, "city": "Grenoble"}
)

assert response.status_code == 400
assert response.json["code"] == "INELIGIBLE_POSTAL_CODE"
assert user.postalCode != postal_code

@time_machine.travel("2022-03-17 09:00:00")
def test_activity_update(self, client):
user = users_factories.UserFactory(email=self.identifier)
Expand Down
24 changes: 23 additions & 1 deletion api/tests/routes/native/v1/subscription_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pcapi.core.users import models as users_models
from pcapi.notifications.push import testing as push_testing
import pcapi.repository
from pcapi.utils.postal_code import INELIGIBLE_POSTAL_CODES
from pcapi.utils.string import u_nbsp


Expand Down Expand Up @@ -1543,8 +1544,29 @@ def test_fulfill_profile_missing_mandatory_field(self, client):

assert response.status_code == 400

@pytest.mark.parametrize("postal_code", INELIGIBLE_POSTAL_CODES)
@override_features(ENABLE_UBBLE=True)
def test_fulfill_profile_valid_character(self, client):
def test_fulfill_profile_ineligible_postal_code(self, client, postal_code):
user = users_factories.UserFactory()

response = client.with_token(user.email).post(
"/native/v1/subscription/profile",
{
"firstName": "John",
"lastName": "Doe",
"address": "1 rue des rues",
"city": "Uneville",
"postalCode": postal_code,
"activityId": "HIGH_SCHOOL_STUDENT",
"schoolTypeId": "PUBLIC_HIGH_SCHOOL",
},
)

assert response.status_code == 400
assert response.json["code"] == "INELIGIBLE_POSTAL_CODE"

@override_features(ENABLE_UBBLE=True)
def test_fulfill_profile_invalid_character(self, client):
user = users_factories.UserFactory(
address=None,
city=None,
Expand Down

0 comments on commit fa45a5c

Please sign in to comment.