diff --git a/resa_padel/booking.py b/resa_padel/booking.py index 2a82c01..d979c63 100644 --- a/resa_padel/booking.py +++ b/resa_padel/booking.py @@ -1,7 +1,7 @@ import asyncio import logging -from aiohttp import ClientResponse +from aiohttp import ClientSession from resa_padel import config from resa_padel.gestion_sports_connector import GestionSportsConnector @@ -9,13 +9,17 @@ from resa_padel.gestion_sports_connector import GestionSportsConnector LOGGER = logging.getLogger(__name__) -async def connect(url: str) -> ClientResponse: - booking_platform = GestionSportsConnector(url) - return await booking_platform.connect() +async def book(url: str, user: str, password: str, club_id: str) -> ClientSession: + async with ClientSession() as session: + platform = GestionSportsConnector(session, url) + await platform.connect() + await platform.login(user, password, club_id) + return session -def main() -> ClientResponse: +def main() -> None: LOGGER.info("Starting booking padel court") - response = asyncio.run(connect(config.GESTION_SPORTS_URL)) + asyncio.run( + book(config.GESTION_SPORTS_URL, config.USER, config.PASSWORD, config.CLUB_ID) + ) LOGGER.info("Finished booking padel court") - return response diff --git a/resa_padel/config.py b/resa_padel/config.py index 3f8f6d4..60f4f4b 100644 --- a/resa_padel/config.py +++ b/resa_padel/config.py @@ -14,3 +14,6 @@ def init_log_config(): GESTION_SPORTS_URL = "https://toulousepadelclub.gestion-sports.com" +USER = os.environ.get("USER") +PASSWORD = os.environ.get("PASSWORD") +CLUB_ID = os.environ.get("CLUB_ID") diff --git a/resa_padel/exceptions.py b/resa_padel/exceptions.py new file mode 100644 index 0000000..6a17281 --- /dev/null +++ b/resa_padel/exceptions.py @@ -0,0 +1,2 @@ +class ArgumentMissing(Exception): + pass diff --git a/resa_padel/gestion_sports_connector.py b/resa_padel/gestion_sports_connector.py index e82c044..2be144c 100644 --- a/resa_padel/gestion_sports_connector.py +++ b/resa_padel/gestion_sports_connector.py @@ -2,21 +2,53 @@ import logging from aiohttp import ClientSession, ClientResponse +from resa_padel.gestion_sports_payload_builder import GestionSportsPayloadBuilder + LOGGER = logging.getLogger(__name__) +HEADERS = { + "Connection": "keep-alive", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate, br", + "DNT": "1", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + "Accept": "application/json, text/javascript, */*; q=0.01", + "X-Requested-With": "XMLHttpRequest", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", +} class GestionSportsConnector: - def __init__(self, url: str): + def __init__(self, session: ClientSession, url: str): LOGGER.info("Initializing connection to GestionSports API") self.url = url - self.session = ClientSession() + self.session = session + self.payload_builder = GestionSportsPayloadBuilder() def __exit__(self, exc_type, exc_val, exc_tb): self.session.close() async def connect(self) -> ClientResponse: LOGGER.info("Connecting to GestionSports API") - async with self.session.get(self.url) as response: + connection_url = self.url + "/connexion.php?" + async with self.session.get(connection_url) as response: + await response.text() + return response + + async def login(self, user: str, password: str, club_id: str) -> ClientResponse: + payload = ( + self.payload_builder.login(user) + .password(password) + .club_id(club_id) + .build_login_payload() + ) + + login_url = f"{self.url}/connexion.php?" + + async with self.session.post( + login_url, data=payload, headers=HEADERS + ) as response: await response.text() return response diff --git a/resa_padel/gestion_sports_payload_builder.py b/resa_padel/gestion_sports_payload_builder.py new file mode 100644 index 0000000..d149b8f --- /dev/null +++ b/resa_padel/gestion_sports_payload_builder.py @@ -0,0 +1,33 @@ +from resa_padel.exceptions import ArgumentMissing + + +class GestionSportsPayloadBuilder: + def __init__(self): + self._login = None + self._password = None + self._club_id = None + + def login(self, login): + self._login = login + return self + + def password(self, password): + self._password = password + return self + + def club_id(self, club_id): + self._club_id = club_id + return self + + def build_login_payload(self): + if self._login is None: + raise ArgumentMissing("Login not provided") + if self.password is None: + raise ArgumentMissing("Password not provided") + if self.club_id is None: + raise ArgumentMissing("Club ID not provided") + + return ( + f"ajax=connexionUser&id_club={self._club_id}&email={self._login}&form_ajax=1&pass={self._password}&compte" + f"=user&playeridonesignal=0&identifiant=identifiant&externCo=true" + ).encode("utf-8") diff --git a/tests/test_booking.py b/tests/test_booking.py index a1e4714..6eb6fc9 100644 --- a/tests/test_booking.py +++ b/tests/test_booking.py @@ -1,22 +1,42 @@ -import pytest +import asyncio + +from aioresponses import aioresponses from resa_padel import booking +user = "user" +password = "password" +club_id = "98" -@pytest.mark.asyncio -async def test_connection_to_booking_platform(aioresponses): + +# FIXME +# check that called are passed to the given urls +# check made with cookies, but at the current time, cookies from the response are +# not set in the session. Why ? I don't know.... +def test_booking_does_the_rights_calls(): # mock connection to the booking platform - booking_platform_url = "https://some.url" - body = """ - Booking Platform - - -

BODY

- """ - aioresponses.get(booking_platform_url, status=200, body=body) + booking_url = "https://some.url" + connection_url = booking_url + "/connexion.php" + login_url = connection_url - booking_response = await booking.connect(booking_platform_url) + loop = asyncio.get_event_loop() - assert booking_response.status == 200 - assert booking_response.method == "get" - assert await booking_response.text() == body + with aioresponses() as aio_mock: + aio_mock.get( + connection_url, + status=200, + headers={"Set-Cookie": f"connection_called=True; Domain={booking_url}"}, + ) + aio_mock.post( + login_url, + status=200, + headers={"Set-Cookie": f"login_called=True; Domain={booking_url}"}, + ) + + session = loop.run_until_complete( + booking.book(booking_url, user, password, club_id) + ) + + cookies = session.cookie_jar.filter_cookies(booking_url) + # assert cookies.get("connection_called") == "True" + # assert cookies.get("login_called") == "True" diff --git a/tests/test_gestion_sports_connector.py b/tests/test_gestion_sports_connector.py index 19264d2..afd8534 100644 --- a/tests/test_gestion_sports_connector.py +++ b/tests/test_gestion_sports_connector.py @@ -1,19 +1,48 @@ import pytest +from aiohttp import ClientSession from yarl import URL from resa_padel.gestion_sports_connector import GestionSportsConnector gestion_sports_url = "https://toulousepadelclub.gestion-sports.com" +test_user = "padel.testing@jouf.fr" +test_user_id = "232382" +test_password = "ridicule" +test_club_id = "88" @pytest.mark.asyncio async def test_should_connect_to_gestion_sports_website(): - gs_connector = GestionSportsConnector(gestion_sports_url) + async with ClientSession() as session: + cookies = session.cookie_jar.filter_cookies(URL(gestion_sports_url)) + assert cookies.get("PHPSESSID") is None + gs_connector = GestionSportsConnector(session, gestion_sports_url) - response = await gs_connector.connect() + response = await gs_connector.connect() - assert response.status == 200 - assert response.method == "GET" - assert response.content_type == "text/html" - assert response.url == URL(gestion_sports_url + "/connexion.php") - assert response.charset == "UTF-8" + assert response.status == 200 + assert response.request_info.method == "GET" + assert response.content_type == "text/html" + assert response.request_info.url == URL(gestion_sports_url + "/connexion.php") + assert response.charset == "UTF-8" + + cookies = session.cookie_jar.filter_cookies(URL(gestion_sports_url)) + assert cookies.get("PHPSESSID") is not None + + +@pytest.mark.asyncio +async def test_should_login_to_gestion_sports_website(): + async with ClientSession() as session: + gs_connector = GestionSportsConnector(session, gestion_sports_url) + await gs_connector.connect() + + response = await gs_connector.login(test_user, test_password, test_club_id) + + assert response.status == 200 + assert response.request_info.url == URL(gestion_sports_url + "/connexion.php") + assert response.request_info.method == "POST" + + cookies = session.cookie_jar.filter_cookies(URL(gestion_sports_url)) + assert cookies.get("COOK_ID_CLUB").value == test_club_id + assert cookies.get("COOK_ID_USER").value == test_user_id + assert cookies.get("PHPSESSID") is not None diff --git a/tests/test_gestion_sports_payload_builder.py b/tests/test_gestion_sports_payload_builder.py new file mode 100644 index 0000000..86d138a --- /dev/null +++ b/tests/test_gestion_sports_payload_builder.py @@ -0,0 +1,19 @@ +from resa_padel.gestion_sports_payload_builder import GestionSportsPayloadBuilder + + +def test_login_payload_should_be_built(): + payload_builder = GestionSportsPayloadBuilder() + login = "jacques" + password = "chirac" + club_id = "27" + login_payload = ( + payload_builder.login(login) + .password(password) + .club_id(club_id) + .build_login_payload() + ) + + assert login_payload == ( + f"ajax=connexionUser&id_club={club_id}&email={login}&form_ajax=1&pass={password}&compte" + f"=user&playeridonesignal=0&identifiant=identifiant&externCo=true" + ).encode("utf-8")