Skip to content

Commit

Permalink
feat(API): allow adding prices on proofs not owned (only PRICE_TAG pr…
Browse files Browse the repository at this point in the history
…oofs) (#609)
  • Loading branch information
raphodn authored Dec 6, 2024
1 parent 0c68bf8 commit 071fad0
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 22 deletions.
42 changes: 27 additions & 15 deletions open_prices/api/prices/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ class PriceListFilterApiTest(TestCase):
def setUpTestData(cls):
cls.url = reverse("api:prices-list")
cls.user_session = SessionFactory()
cls.user_proof = ProofFactory(
cls.user_proof_receipt = ProofFactory(
type=proof_constants.TYPE_RECEIPT, owner=cls.user_session.user.user_id
)
cls.user_price = PriceFactory(
**PRICE_8001505005707,
receipt_quantity=2,
proof_id=cls.user_proof.id,
proof_id=cls.user_proof_receipt.id,
owner=cls.user_session.user.user_id,
)
PriceFactory(
Expand Down Expand Up @@ -327,15 +327,16 @@ class PriceCreateApiTest(TestCase):
def setUpTestData(cls):
cls.url = reverse("api:prices-list")
cls.user_session = SessionFactory()
cls.user_proof = ProofFactory(
cls.user_proof_gdpr = ProofFactory(
type=proof_constants.TYPE_RECEIPT, owner=cls.user_session.user.user_id
)
cls.proof_2 = ProofFactory()
cls.proof_price_tag = ProofFactory(type=proof_constants.TYPE_PRICE_TAG)
cls.proof_receipt = ProofFactory(type=proof_constants.TYPE_RECEIPT)
cls.data = {
**PRICE_8001505005707,
"location_osm_id": 652825274,
"location_osm_type": "NODE",
"proof_id": cls.user_proof.id,
"proof_id": cls.user_proof_gdpr.id,
"source": "test",
}

Expand Down Expand Up @@ -383,14 +384,6 @@ def test_price_create_with_proof(self):
content_type="application/json",
)
self.assertEqual(response.status_code, 400)
# not proof owner
response = self.client.post(
self.url,
{**self.data, "proof_id": self.proof_2.id},
headers={"Authorization": f"Bearer {self.user_session.token}"},
content_type="application/json",
)
self.assertEqual(response.status_code, 400)
# authenticated
response = self.client.post(
self.url,
Expand All @@ -408,11 +401,11 @@ def test_price_create_with_proof(self):
self.assertEqual(response.data["owner"], self.user_session.user.user_id)
# with proof, product & location
self.assertTrue("proof_id" in response.data)
self.assertEqual(response.data["proof"]["id"], self.user_proof.id)
self.assertEqual(response.data["proof"]["id"], self.user_proof_gdpr.id)
self.assertEqual(
response.data["proof"]["price_count"], 0
) # not yet incremented
self.assertEqual(Proof.objects.get(id=self.user_proof.id).price_count, 1)
self.assertEqual(Proof.objects.get(id=self.user_proof_gdpr.id).price_count, 1)
self.assertTrue("product_id" in response.data)
self.assertEqual(response.data["product"]["code"], "8001505005707")
self.assertEqual(
Expand All @@ -430,6 +423,25 @@ def test_price_create_with_proof(self):
p = Price.objects.last()
self.assertEqual(p.source, "API") # default value

def test_price_create_with_proof_not_owned(self):
# not proof owner and proof is not a PRICE_TAG: NOK
response = self.client.post(
self.url,
{**self.data, "proof_id": self.proof_receipt.id},
headers={"Authorization": f"Bearer {self.user_session.token}"},
content_type="application/json",
)
self.assertEqual(response.status_code, 400)
# not proof owner and proof is a PRICE_TAG: OK !
response = self.client.post(
self.url,
{**self.data, "proof_id": self.proof_price_tag.id},
headers={"Authorization": f"Bearer {self.user_session.token}"},
content_type="application/json",
)
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data["owner"], self.user_session.user.user_id)

def test_price_create_with_location_id(self):
location_osm = LocationFactory(**LOCATION_OSM_NODE_652825274)
location_online = LocationFactory(type=location_constants.TYPE_ONLINE)
Expand Down
1 change: 1 addition & 0 deletions open_prices/prices/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ class Params:
)
location_osm_type = factory.fuzzy.FuzzyChoice(location_constants.OSM_TYPE_LIST)
date = date.fromisoformat("2023-10-30")
# owner = factory.Faker("user_name")
8 changes: 6 additions & 2 deletions open_prices/prices/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,11 +463,15 @@ def clean(self, *args, **kwargs):
)

if proof:
if proof.owner != self.owner:
if (
proof.owner != self.owner
and proof.type
not in proof_constants.TYPE_ALLOW_ANY_USER_PRICE_ADD_LIST
):
validation_errors = utils.add_validation_error(
validation_errors,
"proof",
"Proof does not belong to the current user",
"Proof does not belong to the current user. Adding a price to a proof a user does not own is only allowed for {proof_constants.TYPE_ALLOW_ANY_USER_PRICE_ADD_LIST} proofs",
)
if not self.id: # skip these checks on update
if proof.type in proof_constants.TYPE_SINGLE_SHOP_LIST:
Expand Down
25 changes: 21 additions & 4 deletions open_prices/prices/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,14 @@ def test_price_proof_validation(self):
currency="EUR",
owner=user_session.user.user_id,
)
proof_2 = ProofFactory()
proof_gdpr = ProofFactory(
type=proof_constants.TYPE_GDPR_REQUEST,
location_osm_id=169450062,
location_osm_type=location_constants.OSM_TYPE_NODE,
date="2024-10-01",
currency="EUR",
)
proof_price_tag = ProofFactory(type=proof_constants.TYPE_PRICE_TAG)
# proof not set
PriceFactory(proof_id=None, owner=user_proof_receipt.owner)
# proof unknown
Expand All @@ -374,16 +381,26 @@ def test_price_proof_validation(self):
currency=user_proof_receipt.currency,
owner=user_proof_receipt.owner,
)
# different price & proof owner
# difference price and proof owner: raise a ValidationError
# if the proof type is different than PRICE_TAG
self.assertRaises(
ValidationError,
PriceFactory,
proof_id=proof_2.id, # different
proof_id=proof_gdpr.id, # gdpr proof
location_osm_id=proof_gdpr.location_osm_id,
location_osm_type=proof_gdpr.location_osm_type,
date=proof_gdpr.date,
currency=proof_gdpr.currency,
owner=user_proof_receipt.owner, # different owner
)
# different price & proof owner: ok for PRICE_TAG proofs
PriceFactory(
proof_id=proof_price_tag.id,
location_osm_id=user_proof_receipt.location_osm_id,
location_osm_type=user_proof_receipt.location_osm_type,
date=user_proof_receipt.date,
currency=user_proof_receipt.currency,
owner=user_proof_receipt.owner,
owner=user_proof_receipt.owner, # different owner
)
# proof location_osm_id & location_osm_type
self.assertRaises(
Expand Down
6 changes: 5 additions & 1 deletion open_prices/proofs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
TYPE_LIST = [TYPE_PRICE_TAG, TYPE_RECEIPT, TYPE_GDPR_REQUEST, TYPE_SHOP_IMPORT]
TYPE_CHOICES = [(key, key) for key in TYPE_LIST]

# 1 proof = 1 shop + 1 date
# SINGLE_SHOP: same location, date & currency
TYPE_SINGLE_SHOP_LIST = [TYPE_PRICE_TAG, TYPE_RECEIPT, TYPE_SHOP_IMPORT]
# SHOPPING_SESSION: extra fields
TYPE_SHOPPING_SESSION_LIST = [TYPE_RECEIPT, TYPE_GDPR_REQUEST]
# MULTIPLE_SHOP
TYPE_MULTIPLE_SHOP_LIST = [TYPE_GDPR_REQUEST]
# ALLOW_ANY_USER_PRICE_ADD
TYPE_ALLOW_ANY_USER_PRICE_ADD_LIST = [TYPE_PRICE_TAG]

PROOF_PREDICTION_OBJECT_DETECTION_TYPE = "OBJECT_DETECTION"
PROOF_PREDICTION_CLASSIFICATION_TYPE = "CLASSIFICATION"
Expand Down
1 change: 1 addition & 0 deletions open_prices/proofs/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Meta:
# date = factory.Faker("date")
# currency = factory.Faker("currency_symbol")
# price_count = factory.LazyAttribute(lambda x: random.randrange(0, 100))
# owner = factory.Faker("user_name")


class ProofPredictionFactory(DjangoModelFactory):
Expand Down

0 comments on commit 071fad0

Please sign in to comment.