diff --git a/src/api/CHANGELOG.md b/src/api/CHANGELOG.md index 9499b8da..76c8b1f9 100644 --- a/src/api/CHANGELOG.md +++ b/src/api/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to number of database queries - CLI: sort groups and operational units alphabetically in the `list-groups` command +- Decrease the number of database queries for dynamic endpoints ## [0.16.0] - 2024-12-12 diff --git a/src/api/qualicharge/api/v1/routers/dynamic.py b/src/api/qualicharge/api/v1/routers/dynamic.py index 13d0c735..9ba8bee5 100644 --- a/src/api/qualicharge/api/v1/routers/dynamic.py +++ b/src/api/qualicharge/api/v1/routers/dynamic.py @@ -324,11 +324,13 @@ async def create_status( detail="Attached point of charge does not exist", ) db_status = Status(**status.model_dump(exclude={"id_pdc_itinerance"})) + # Store status id so that we do not need to perform another request + db_status_id = db_status.id db_status.point_de_charge_id = pdc_id session.add(db_status) session.commit() - return DynamiqueItemCreatedResponse(id=db_status.id) + return DynamiqueItemCreatedResponse(id=db_status_id) @router.post("/status/bulk", status_code=fa_status.HTTP_201_CREATED, tags=["Status"]) @@ -365,16 +367,18 @@ async def create_status_bulk( # Create all statuses db_statuses = [] + db_status_ids = [] for status in statuses: db_status = Status(**status.model_dump(exclude={"id_pdc_itinerance"})) + db_status_ids.append(db_status.id) db_status.point_de_charge_id = db_pdcs[status.id_pdc_itinerance] db_statuses.append(db_status) session.add_all(db_statuses) session.commit() return DynamiqueItemsCreatedResponse( - size=len(db_statuses), - items=[s.id for s in db_statuses], + size=len(db_status_ids), + items=db_status_ids, ) @@ -403,11 +407,13 @@ async def create_session( detail="Attached point of charge does not exist", ) db_qc_session = QCSession(**session.model_dump(exclude={"id_pdc_itinerance"})) + # Store session id so that we do not need to perform another request + db_qc_session_id = db_qc_session.id db_qc_session.point_de_charge_id = pdc_id db_session.add(db_qc_session) db_session.commit() - return DynamiqueItemCreatedResponse(id=db_qc_session.id) + return DynamiqueItemCreatedResponse(id=db_qc_session_id) @router.post("/session/bulk", status_code=fa_status.HTTP_201_CREATED, tags=["Session"]) @@ -444,14 +450,16 @@ async def create_session_bulk( # Create all statuses db_qc_sessions = [] + db_qc_session_ids = [] for session in sessions: db_qc_session = QCSession(**session.model_dump(exclude={"id_pdc_itinerance"})) + db_qc_session_ids.append(db_qc_session.id) db_qc_session.point_de_charge_id = db_pdcs[session.id_pdc_itinerance] db_qc_sessions.append(db_qc_session) db_session.add_all(db_qc_sessions) db_session.commit() return DynamiqueItemsCreatedResponse( - size=len(db_qc_sessions), - items=[s.id for s in db_qc_sessions], + size=len(db_qc_session_ids), + items=db_qc_session_ids, ) diff --git a/src/api/tests/api/v1/routers/test_dynamic.py b/src/api/tests/api/v1/routers/test_dynamic.py index 72478f22..d3edbeb7 100644 --- a/src/api/tests/api/v1/routers/test_dynamic.py +++ b/src/api/tests/api/v1/routers/test_dynamic.py @@ -15,6 +15,7 @@ from qualicharge.auth.factories import GroupFactory from qualicharge.auth.schemas import GroupOperationalUnit, ScopesEnum, User from qualicharge.conf import settings +from qualicharge.db import SAQueryCounter from qualicharge.factories.dynamic import ( SessionCreateFactory, StatusCreateFactory, @@ -763,6 +764,30 @@ def test_create_status_for_superuser(db_session, client_auth): assert response.json() == {"id": str(db_status.id)} +def test_create_status_number_of_queries(db_session, client_auth): + """Test the /status/ create endpoint number of db queries.""" + id_pdc_itinerance = "FR911E1111ER1" + qc_status = StatusCreateFactory.build(id_pdc_itinerance=id_pdc_itinerance) + + # Create point of charge + save_statique( + db_session, StatiqueFactory.build(id_pdc_itinerance=id_pdc_itinerance) + ) + + # Create a new status + with SAQueryCounter(db_session.connection()) as counter: + response = client_auth.post( + "/dynamique/status/", json=json.loads(qc_status.model_dump_json()) + ) + assert response.status_code == status.HTTP_201_CREATED + # We expect 3 database requests: + # 1. select request user + # 2. select point of charge + # 3. insert status + expected = 3 + assert counter.count == expected + + @pytest.mark.parametrize( "client_auth", ( @@ -938,6 +963,37 @@ def test_create_status_bulk_for_superuser(db_session, client_auth): assert db_status.etat_prise_type_ef == qc_status.etat_prise_type_ef +def test_create_status_bulk_number_of_queries(db_session, client_auth): + """Test the /status/bulk create endpoint number of db queries.""" + qc_statuses = StatusCreateFactory.batch(3) + + # Create points of charge + save_statiques( + db_session, + [ + StatiqueFactory.build(id_pdc_itinerance=s.id_pdc_itinerance) + for s in qc_statuses + ], + ) + + # Assert no status exist + assert db_session.exec(select(func.count(Status.id))).one() == 0 + + # We expect the same answer as one point of charge does not exist + with SAQueryCounter(db_session.connection()) as counter: + response = client_auth.post( + "/dynamique/status/bulk", + json=[json.loads(s.model_dump_json()) for s in qc_statuses], + ) + assert response.status_code == status.HTTP_201_CREATED + # We expect 3 database requests: + # 1. select request user + # 2. select points of charge + # 3. insert statuses + expected = 3 + assert counter.count == expected + + @pytest.mark.parametrize( "client_auth", ( @@ -1178,6 +1234,30 @@ def test_create_session_for_superuser(db_session, client_auth): assert response.json() == {"id": str(db_qc_session.id)} +def test_create_session_number_of_queries(db_session, client_auth): + """Test the /session/ create endpoint number of db queries.""" + id_pdc_itinerance = "FR911E1111ER1" + qc_session = SessionCreateFactory.build(id_pdc_itinerance=id_pdc_itinerance) + + # Create point of charge + save_statique( + db_session, StatiqueFactory.build(id_pdc_itinerance=id_pdc_itinerance) + ) + + # Create a new status + with SAQueryCounter(db_session.connection()) as counter: + response = client_auth.post( + "/dynamique/session/", json=json.loads(qc_session.model_dump_json()) + ) + assert response.status_code == status.HTTP_201_CREATED + # We expect 3 database requests: + # 1. select request user + # 2. select point of charge + # 3. insert session + expected = 3 + assert counter.count == expected + + @pytest.mark.parametrize( "client_auth", ( @@ -1348,6 +1428,37 @@ def test_create_session_bulk_for_superuser(db_session, client_auth): assert db_qc_session.energy == qc_session.energy +def test_create_session_bulk_number_of_queries(db_session, client_auth): + """Test the /session/bulk create endpoint number of db queries.""" + qc_sessions = SessionCreateFactory.batch(3) + + # Create points of charge + save_statiques( + db_session, + [ + StatiqueFactory.build(id_pdc_itinerance=s.id_pdc_itinerance) + for s in qc_sessions + ], + ) + + # Assert no session exist + assert db_session.exec(select(func.count(Session.id))).one() == 0 + + # We expect the same answer as one point of charge does not exist + with SAQueryCounter(db_session.connection()) as counter: + response = client_auth.post( + "/dynamique/session/bulk", + json=[json.loads(s.model_dump_json()) for s in qc_sessions], + ) + assert response.status_code == status.HTTP_201_CREATED + # We expect 3 database requests: + # 1. select request user + # 2. select points of charge + # 3. insert sessions + expected = 3 + assert counter.count == expected + + @pytest.mark.parametrize( "client_auth", (