Skip to content
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

[experimental] l'utente può vedere anche le proprie prenotazioni fatte da anonimo ma usando il proprio codice fiscale #209

Merged
merged 1 commit into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
},
)