Skip to content

Commit

Permalink
SEE_OWN_ANONYMOUS_BOOKINGS (#209)
Browse files Browse the repository at this point in the history
  • Loading branch information
mamico authored Jun 13, 2024
1 parent 0831e61 commit 3d6050c
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 5 deletions.
4 changes: 3 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Changelog
2.7.3 (unreleased)
------------------

- Nothing changed yet.
- With an experimental envionment `SEE_OWN_ANONYMOUS_BOOKINGS` set to `True`, the endpoint will return
also the bookings created by anonymous users with the same fiscalcode of the authenticated user.
[mamico]


2.7.2 (2024-06-03)
Expand Down
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,10 @@ Response::
}
}

If a user is authenticated and, he is not a site operator, returns all own bookings.

With an experimental envionment `SEE_OWN_ANONYMOUS_BOOKINGS` set to `True`, the endpoint will return
also the bookings created by anonymous users with the same fiscalcode of the authenticated user.

@booking-notify
---------------
Expand Down
32 changes: 28 additions & 4 deletions src/redturtle/prenotazioni/restapi/services/bookings/search.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import logging
import os

from DateTime import DateTime
from plone import api
Expand All @@ -12,6 +13,11 @@
from redturtle.prenotazioni.interfaces import ISerializeToPrenotazioneSearchableItem

logger = logging.getLogger(__name__)
SEE_OWN_ANONYMOUS_BOOKINGS = os.environ.get("SEE_OWN_ANONYMOUS_BOOKINGS") in [
"True",
"true",
"1",
]


@implementer(IPublishTraverse)
Expand Down Expand Up @@ -84,26 +90,44 @@ def query(self):
return query

def reply(self):
# XXX: `fullobjects` the correct behavior should be to use different serializers
fullobjects = self.request.form.get("fullobjects", False) == "1"
response = {"id": self.context.absolute_url() + "/@bookings"}
query = self.query()
# XXX: `fullobjects` the correct behavior should be to use different serializers
items = []
for i in api.portal.get_tool("portal_catalog")(**query):
if query.get("fiscalcode") and SEE_OWN_ANONYMOUS_BOOKINGS:
# brains = api.content.find(**query, unrestricted=True)
brains = api.portal.get_tool("portal_catalog").unrestrictedSearchResults(
**query
)
unrestricted = True
else:
# brains = api.content.find(**query)
brains = api.portal.get_tool("portal_catalog")(**query)
unrestricted = False
for brain in brains:
# TEMP: errors with broken catalog entries
try:
items.append(
getMultiAdapter(
(i.getObject(), self.request),
(self.wrappedGetObject(brain, unrestricted), self.request),
ISerializeToPrenotazioneSearchableItem,
)(fullobjects=fullobjects)
)
except: # noqa: E722
logger.exception("error with %s", i.getPath())
logger.exception("error with %s", brain.getPath())
response["items"] = items
response["items_total"] = len(response["items"])
return response

@staticmethod
def wrappedGetObject(brain, unrestricted=False):
if unrestricted:
# with api.env.adopt_user(brain.Creator):
with api.env.adopt_roles("Manager"):
return brain.getObject()
return brain.getObject()


class BookingsSearchFolder(BookingsSearch):
def query(self):
Expand Down
199 changes: 199 additions & 0 deletions src/redturtle/prenotazioni/tests/test_prenotazioni_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,3 +503,202 @@ def test_download_xlsx_sort(self):
[r[0].value for r in data["Sheet 1"].rows][1:],
sorted([r[0].value for r in data["Sheet 1"].rows][1:], reverse=True),
)


class TestPrenotazioniUserSearch(unittest.TestCase):
"""Test the restapi search endpoint (<portal_url>/@bookings)"""

layer = REDTURTLE_PRENOTAZIONI_API_FUNCTIONAL_TESTING

def setUp(self):
self.app = self.layer["app"]
self.portal = self.layer["portal"]
self.portal_url = self.portal.absolute_url()
self.testing_fiscal_code = "TESTINGFISCALCODE"
self.testing_booking_date = parser.parse("2023-04-28 16:00:00")
self.booking_expiration_date = parser.parse("2023-04-28 16:00:00") + timedelta(
days=100
)

setRoles(self.portal, TEST_USER_ID, ["Manager"])

api.user.create(
email="[email protected]",
username="TESTINGFISCALCODE",
password="testingpassowrd",
properties={"fiscalcode": "TESTINGFISCALCODE"},
)

self.api_session = RelativeSession(self.portal_url)
self.api_session.headers.update({"Accept": "application/json"})
self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

self.anon_session = RelativeSession(self.portal_url)
self.anon_session.headers.update({"Accept": "application/json"})
self.anon_session.auth = None

self.user_session = RelativeSession(self.portal_url)
self.user_session.headers.update({"Accept": "application/json"})
self.user_session.auth = ("TESTINGFISCALCODE", "testingpassowrd")

self.browser = Browser(self.layer["app"])

self.folder_prenotazioni = api.content.create(
container=self.portal,
type="PrenotazioniFolder",
title="Prenota foo",
description="",
daData=date.today(),
gates=["Gate A"],
)
api.content.transition(obj=self.folder_prenotazioni, transition="publish")

obj = api.content.create(
type="PrenotazioneType",
title="Type A",
duration=30,
container=self.folder_prenotazioni,
gates=["all"],
requirements=RichTextValue(
"You need to bring your own food", "text/plain", "text/html"
),
)
api.content.transition(obj=obj, transition="publish")

week_table = deepcopy(self.folder_prenotazioni.week_table)
week_table[0]["morning_start"] = "0700"
week_table[0]["morning_end"] = "1000"
self.folder_prenotazioni.week_table = week_table

self.folder_prenotazioni2 = api.content.create(
container=self.portal,
type="PrenotazioniFolder",
title="Prenota bar",
description="",
daData=date.today(),
gates=["Gate A"],
)
api.content.transition(obj=self.folder_prenotazioni2, transition="publish")

obj = api.content.create(
type="PrenotazioneType",
title="Type A",
duration=10,
container=self.folder_prenotazioni2,
gates=["all"],
)
api.content.transition(obj=obj, transition="publish")

obj = api.content.create(
type="PrenotazioneType",
title="Type B",
duration=20,
container=self.folder_prenotazioni2,
gates=["all"],
)
api.content.transition(obj=obj, transition="publish")

week_table = deepcopy(self.folder_prenotazioni2.week_table)
week_table[0]["morning_start"] = "0700"
week_table[0]["morning_end"] = "1000"
self.folder_prenotazioni2.week_table = week_table

transaction.commit()

def tearDown(self):
self.api_session.close()
self.anon_session.close()
self.user_session.close()

def test_search_own_bookings(self):

# booking_date = "{}T09:00:00+00:00".format(
# (date.today() + timedelta(1)).strftime("%Y-%m-%d")
# )
res = self.anon_session.get(
f"{self.folder_prenotazioni.absolute_url()}/@available-slots"
)
booking_date = res.json()["items"][0]
# anonymous user 1
res = self.add_booking(
self.anon_session,
booking_date=booking_date,
booking_type="Type A",
fields=[
{"name": "title", "value": "Mario Rossi"},
{"name": "email", "value": "mario.rossi@example"},
{"name": "fiscalcode", "value": "ABCDEF12G34H567I"},
],
)
self.assertEqual(res.status_code, 200)
# anonymous user 2 (not the same fiscalcode, pretend to be john)
res = self.anon_session.get(
f"{self.folder_prenotazioni.absolute_url()}/@available-slots"
)
booking_date = res.json()["items"][0]
res = self.add_booking(
self.anon_session,
booking_date=booking_date,
booking_type="Type A",
fields=[
{"name": "title", "value": "John Rossi"},
{"name": "email", "value": "john@example"},
{"name": "fiscalcode", "value": "TESTINGFISCALCODE"},
],
)
self.assertEqual(res.status_code, 200)
# john
res = self.anon_session.get(
f"{self.folder_prenotazioni.absolute_url()}/@available-slots"
)
booking_date = res.json()["items"][0]
res = self.add_booking(
self.user_session,
booking_date=booking_date,
booking_type="Type A",
fields=[
{"name": "title", "value": "John Rossi"},
{"name": "email", "value": "john@example"},
# {"name": "fiscalcode", "value": "TESTINGFISCALCODE"},
],
)
self.assertEqual(res.status_code, 200)

# anonimo non può vedere le prenotazioni
res = self.anon_session.get(
f"{self.portal.absolute_url()}/@bookings/TESTINGFISCALCODE"
)
self.assertEqual(res.status_code, 401)

# il manager vede tutte le prenotazioni (3)
res = self.api_session.get(f"{self.portal.absolute_url()}/@bookings")
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json()["items_total"], 3)

# la prenotazione fatta da anonimo con il codicefiscale di john non è visibile
res = self.user_session.get(f"{self.portal.absolute_url()}/@bookings")
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json()["items_total"], 1)

# XXX: monkeypatching per vedere le prenotazioni anonime
from redturtle.prenotazioni.restapi.services.bookings import search

search.SEE_OWN_ANONYMOUS_BOOKINGS = True
res = self.user_session.get(f"{self.portal.absolute_url()}/@bookings")
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json()["items_total"], 2)
search.SEE_OWN_ANONYMOUS_BOOKINGS = False

# TODO: verificare che siano le prenotazioni giuste

# utility methods

def add_booking(self, api_session, booking_date, booking_type, fields):
return api_session.post(
f"{self.folder_prenotazioni.absolute_url()}/@booking",
json={
"booking_date": booking_date,
"booking_type": booking_type,
"fields": fields,
},
)

0 comments on commit 3d6050c

Please sign in to comment.