-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fixed offer permissions #343
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,15 +55,24 @@ def get_queryset(self): | |
|
||
|
||
# TODO: Can add feature to filter for active offers only | ||
class UserOffers(generics.ListAPIView): | ||
class OffersMade(generics.ListAPIView): | ||
serializer_class = OfferSerializer | ||
permission_classes = [IsAuthenticated] | ||
permission_classes = [IsAuthenticated | IsSuperUser] | ||
|
||
def get_queryset(self): | ||
user = self.request.user | ||
return Offer.objects.filter(user=user) | ||
|
||
|
||
class OffersReceived(generics.ListAPIView): | ||
serializer_class = OfferSerializer | ||
permission_classes = [IsAuthenticated | IsSuperUser] | ||
|
||
def get_queryset(self): | ||
user = self.request.user | ||
return Offer.objects.filter(item__seller=user) | ||
|
||
|
||
class Items(viewsets.ModelViewSet): | ||
""" | ||
list: | ||
|
@@ -299,5 +308,6 @@ def destroy(self, request, *args, **kwargs): | |
def list(self, request, *args, **kwargs): | ||
if not Item.objects.filter(pk=int(self.kwargs["item_id"])).exists(): | ||
raise exceptions.NotFound("No Item matches the given query") | ||
self.check_object_permissions(request, Item.objects.get(pk=int(self.kwargs["item_id"]))) | ||
for offer in self.get_queryset(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On another note, do you have to manually raise NotFound exception? If you do, shouldn't you be raising in the queryset? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like we don't need to manually override list if we do the queryset and object permissions right |
||
self.check_object_permissions(request, offer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dont think you need to check manually... won't setting the permission class already check? |
||
return super().list(request, *args, **kwargs) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,6 +151,10 @@ def setUp(self): | |
user=user1, item=Item.objects.get(id=5), email="[email protected]" | ||
) | ||
created_offer_3.save() | ||
created_offer_4 = Offer.objects.create( | ||
user=self.user, item=Item.objects.get(id=4), email="[email protected]" | ||
) | ||
created_offer_4.save() | ||
|
||
storage_mock = MagicMock(spec=Storage, name="StorageMock") | ||
storage_mock.generate_filename = lambda filename: filename | ||
|
@@ -169,10 +173,9 @@ def test_get_items(self): | |
"category": "Book", | ||
"title": "Math Textbook", | ||
"price": 20.0, | ||
"negotiable": True, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
{ | ||
"id": 2, | ||
|
@@ -181,10 +184,9 @@ def test_get_items(self): | |
"category": "Food", | ||
"title": "Bag of Doritos", | ||
"price": 5.0, | ||
"negotiable": False, | ||
"expires_at": "2025-10-12T01:00:00-04:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
{ | ||
"id": 3, | ||
|
@@ -193,10 +195,9 @@ def test_get_items(self): | |
"category": "Electronics", | ||
"title": "Macbook Pro", | ||
"price": 2000.0, | ||
"negotiable": True, | ||
"expires_at": "2025-08-12T01:00:00-04:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
{ | ||
"id": 4, | ||
|
@@ -205,10 +206,9 @@ def test_get_items(self): | |
"category": "Furniture", | ||
"title": "Couch", | ||
"price": 400.0, | ||
"negotiable": True, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorites": [], | ||
"favorite_count": 0, | ||
}, | ||
] | ||
self.assertEqual(response.status_code, 200) | ||
|
@@ -217,6 +217,24 @@ def test_get_items(self): | |
sorted(expected_response, key=lambda d: d["id"]), | ||
) | ||
|
||
def test_get_item_seller(self): | ||
response = self.client.get("/market/items/?seller=true") | ||
expected_response = [ | ||
{ | ||
"id": 1, | ||
"seller": 1, | ||
"tags": ["Textbook", "Used"], | ||
"category": "Book", | ||
"title": "Math Textbook", | ||
"price": 20.0, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorite_count": 1, | ||
} | ||
] | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json(), expected_response) | ||
|
||
def test_get_single_item_own(self): | ||
response = self.client.get("/market/items/1/") | ||
response_without_created_at = response.json().copy() | ||
|
@@ -367,6 +385,20 @@ def test_create_item_exclude_unrequired(self): | |
datetime.timedelta(minutes=10), | ||
) | ||
|
||
def test_create_item_missing_filed(self): | ||
payload = { | ||
"tags": ["New"], | ||
"category": "Book", | ||
"external_link": "https://example.com/listing", | ||
"price": 20.0, | ||
"negotiable": True, | ||
"expires_at": "2024-12-12T00:00:00-05:00", | ||
} | ||
response = self.client.post("/market/items/", payload) | ||
res_json = json.loads(response.content) | ||
self.assertEqual(response.status_code, 400) | ||
self.assertEqual(res_json, {"title": ["This field is required."]}) | ||
|
||
def test_create_item_invalid_category(self): | ||
payload = { | ||
"tags": ["New"], | ||
|
@@ -622,10 +654,9 @@ def test_get_sublets(self): | |
"category": "Sublet", | ||
"title": "Cira Green Sublet", | ||
"price": 1350.0, | ||
"negotiable": False, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorites": [], | ||
"favorite_count": 0, | ||
}, | ||
"address": "Cira Green, Philadelphia, PA", | ||
"beds": 3.0, | ||
|
@@ -642,10 +673,9 @@ def test_get_sublets(self): | |
"category": "Sublet", | ||
"title": "Rodin Quad", | ||
"price": 1350.0, | ||
"negotiable": False, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
"address": "3901 Locust Walk, Philadelphia, PA", | ||
"beds": 4.0, | ||
|
@@ -654,7 +684,10 @@ def test_get_sublets(self): | |
"end_date": "2025-05-31T00:00:00-04:00", | ||
}, | ||
] | ||
self.assertEqual(response.json(), expected_response) | ||
self.assertEqual( | ||
sorted(response.json(), key=lambda d: d["id"]), | ||
sorted(expected_response, key=lambda d: d["id"]), | ||
) | ||
|
||
def test_get_sublet_own(self): | ||
response = self.client.get("/market/sublets/1/") | ||
|
@@ -1134,10 +1167,9 @@ def test_get_all_user_favorites(self): | |
"category": "Book", | ||
"title": "Math Textbook", | ||
"price": 20.0, | ||
"negotiable": True, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
{ | ||
"id": 2, | ||
|
@@ -1146,10 +1178,9 @@ def test_get_all_user_favorites(self): | |
"category": "Food", | ||
"title": "Bag of Doritos", | ||
"price": 5.0, | ||
"negotiable": False, | ||
"expires_at": "2025-10-12T01:00:00-04:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
{ | ||
"id": 3, | ||
|
@@ -1158,10 +1189,9 @@ def test_get_all_user_favorites(self): | |
"category": "Electronics", | ||
"title": "Macbook Pro", | ||
"price": 2000.0, | ||
"negotiable": True, | ||
"expires_at": "2025-08-12T01:00:00-04:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
{ | ||
"id": 6, | ||
|
@@ -1170,17 +1200,16 @@ def test_get_all_user_favorites(self): | |
"category": "Sublet", | ||
"title": "Rodin Quad", | ||
"price": 1350.0, | ||
"negotiable": False, | ||
"expires_at": "2025-12-12T00:00:00-05:00", | ||
"images": [], | ||
"favorites": [1], | ||
"favorite_count": 1, | ||
}, | ||
] | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json(), expected_response) | ||
|
||
def test_get_all_user_offers(self): | ||
response = self.client.get("/market/offers/") | ||
response = self.client.get("/market/offers/made/") | ||
response_without_created_at = [offer.copy() for offer in response.json()] | ||
created_at_list = [ | ||
datetime.datetime.fromisoformat(offer.pop("created_at")) | ||
|
@@ -1204,6 +1233,56 @@ def test_get_all_user_offers(self): | |
"user": 1, | ||
"item": 5, | ||
}, | ||
{ | ||
"id": 4, | ||
"phone_number": None, | ||
"email": "[email protected]", | ||
"message": "", | ||
"user": 1, | ||
"item": 4, | ||
}, | ||
] | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response_without_created_at, expected_response) | ||
for created_at in created_at_list: | ||
self.assertLessEqual( | ||
abs(created_at - datetime.datetime.now(pytz.timezone("UTC"))), | ||
datetime.timedelta(minutes=1), | ||
) | ||
|
||
def test_get_all_user_offers_received(self): | ||
response = self.client.get("/market/offers/received/") | ||
response_without_created_at = [offer.copy() for offer in response.json()] | ||
created_at_list = [ | ||
datetime.datetime.fromisoformat(offer.pop("created_at")) | ||
for offer in response_without_created_at | ||
] | ||
|
||
expected_response = [ | ||
{ | ||
"id": 1, | ||
"phone_number": None, | ||
"email": "[email protected]", | ||
"message": "", | ||
"user": 1, | ||
"item": 1, | ||
}, | ||
{ | ||
"id": 2, | ||
"phone_number": None, | ||
"email": "[email protected]", | ||
"message": "", | ||
"user": 1, | ||
"item": 5, | ||
}, | ||
{ | ||
"id": 3, | ||
"phone_number": None, | ||
"email": "[email protected]", | ||
"message": "", | ||
"user": 2, | ||
"item": 5, | ||
}, | ||
] | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response_without_created_at, expected_response) | ||
|
@@ -1274,6 +1353,13 @@ def test_list_item_offers(self): | |
datetime.timedelta(minutes=1), | ||
) | ||
|
||
def test_list_item_offers_other(self): | ||
response = self.client.get("/market/items/4/offers/") | ||
self.assertEqual(response.status_code, 403) | ||
self.assertEqual( | ||
response.json(), {"detail": "You do not have permission to perform this action."} | ||
) | ||
|
||
def test_list_item_offers_invalid_item(self): | ||
response = self.client.get("/market/items/100/offers/") | ||
self.assertEqual(response.status_code, 404) | ||
|
@@ -1296,7 +1382,7 @@ def test_create_offer(self): | |
created_at = response_without_created_at.pop("created_at") | ||
created_at = datetime.datetime.fromisoformat(created_at) | ||
expected_response = { | ||
"id": 4, | ||
"id": 5, | ||
"phone_number": "+14252694412", | ||
"email": "[email protected]", | ||
"message": "I am interested in buying this item.", | ||
|
@@ -1316,7 +1402,7 @@ def test_delete_offer(self): | |
self.assertFalse(Offer.objects.filter(id=1).exists()) | ||
|
||
def test_delete_offer_nonexistent(self): | ||
response = self.client.delete("/market/items/4/offers/") | ||
response = self.client.delete("/market/items/6/offers/") | ||
self.assertEqual(response.status_code, 404) | ||
|
||
def test_create_image(self): | ||
|
@@ -1327,6 +1413,14 @@ def test_create_image(self): | |
self.assertTrue(images.exists()) | ||
self.assertEqual(1, images.first().item.id) | ||
|
||
def test_create_image_other_users_item(self): | ||
with open("tests/market/mock_image.jpg", "rb") as image: | ||
response = self.client.post("/market/items/2/images/", {"images": image}) | ||
self.assertEqual(response.status_code, 403) | ||
self.assertEqual( | ||
response.json(), {"detail": "You do not have permission to perform this action."} | ||
) | ||
|
||
def test_create_delete_images(self): | ||
with open("tests/market/mock_image.jpg", "rb") as image: | ||
with open("tests/market/mock_image.jpg", "rb") as image2: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the destroy code on line 303, it seems like you are doing some manual checks to see if the object can be destroyed. Again, I think if we get the permissions right (which I think they are) and the queryset right (which I think you have correct as well), Django's built in destroy should be able to handle all of it for us?