From e0738be68f97e9e1b1cdfc2ba7b292347b1d7e32 Mon Sep 17 00:00:00 2001 From: jules cicurel <180195494+jcicurel-pass@users.noreply.github.com> Date: Thu, 26 Dec 2024 17:00:19 +0100 Subject: [PATCH] (PC-32747)[API] feat: fix schema typing and set startDatetime and endDatetime not null on collective stock --- api/alembic_version_conflict_detection.txt | 2 +- ...k_startDatetime_endDatetime_step_3_of_4.py | 23 +++++++++++++ ...k_startDatetime_endDatetime_step_2_of_4.py | 31 +++++++++++++++++ ...k_startDatetime_endDatetime_step_1_of_4.py | 33 +++++++++++++++++++ ...k_startDatetime_endDatetime_step_4_of_4.py | 27 +++++++++++++++ api/src/pcapi/core/educational/models.py | 4 +-- .../collective_bookings_serialize.py | 6 ++-- ...tiveBookingCollectiveStockResponseModel.ts | 4 +-- .../utils/factories/collectiveApiFactories.ts | 1 + .../Cells/__specs__/BookingOfferCell.spec.tsx | 8 +++++ 10 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 api/src/pcapi/alembic/versions/20241226T163901_4302137d444a_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_3_of_4.py create mode 100644 api/src/pcapi/alembic/versions/20241226T163901_89d6c28597ef_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_2_of_4.py create mode 100644 api/src/pcapi/alembic/versions/20241226T163901_a09ff41c9e94_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_1_of_4.py create mode 100644 api/src/pcapi/alembic/versions/20241226T163901_a8d2c442cc67_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_4_of_4.py diff --git a/api/alembic_version_conflict_detection.txt b/api/alembic_version_conflict_detection.txt index 79870a1dfb6..683ce36a13e 100644 --- a/api/alembic_version_conflict_detection.txt +++ b/api/alembic_version_conflict_detection.txt @@ -1,2 +1,2 @@ f8588023c126 (pre) (head) -b18478ab2ea8 (post) (head) +a8d2c442cc67 (post) (head) diff --git a/api/src/pcapi/alembic/versions/20241226T163901_4302137d444a_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_3_of_4.py b/api/src/pcapi/alembic/versions/20241226T163901_4302137d444a_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_3_of_4.py new file mode 100644 index 00000000000..10f5e4844c7 --- /dev/null +++ b/api/src/pcapi/alembic/versions/20241226T163901_4302137d444a_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_3_of_4.py @@ -0,0 +1,23 @@ +""" +Add NOT NULL constraint on "collective_stock.startDatetime" and "collective_stock.endDatetime" (step 3 of 4) +""" + +from alembic import op + + +# pre/post deployment: post +# revision identifiers, used by Alembic. +revision = "4302137d444a" +down_revision = "89d6c28597ef" +branch_labels: tuple[str] | None = None +depends_on: list[str] | None = None + + +def upgrade() -> None: + op.alter_column("collective_stock", "startDatetime", nullable=False) + op.alter_column("collective_stock", "endDatetime", nullable=False) + + +def downgrade() -> None: + op.alter_column("collective_stock", "startDatetime", nullable=True) + op.alter_column("collective_stock", "endDatetime", nullable=True) diff --git a/api/src/pcapi/alembic/versions/20241226T163901_89d6c28597ef_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_2_of_4.py b/api/src/pcapi/alembic/versions/20241226T163901_89d6c28597ef_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_2_of_4.py new file mode 100644 index 00000000000..f0c03bae59e --- /dev/null +++ b/api/src/pcapi/alembic/versions/20241226T163901_89d6c28597ef_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_2_of_4.py @@ -0,0 +1,31 @@ +""" +Add NOT NULL constraint on "collective_stock.startDatetime" and "collective_stock.endDatetime" (step 2 of 4) +""" + +from alembic import op + +from pcapi import settings + + +# pre/post deployment: post +# revision identifiers, used by Alembic. +revision = "89d6c28597ef" +down_revision = "a09ff41c9e94" +branch_labels: tuple[str] | None = None +depends_on: list[str] | None = None + + +def upgrade() -> None: + with op.get_context().autocommit_block(): + op.execute("SET SESSION statement_timeout = '300s'") + op.execute( + 'ALTER TABLE "collective_stock" VALIDATE CONSTRAINT "collective_stock_startDatetime_not_null_constraint"' + ) + op.execute( + 'ALTER TABLE "collective_stock" VALIDATE CONSTRAINT "collective_stock_endDatetime_not_null_constraint"' + ) + op.execute(f"SET SESSION statement_timeout={settings.DATABASE_STATEMENT_TIMEOUT}") + + +def downgrade() -> None: + pass diff --git a/api/src/pcapi/alembic/versions/20241226T163901_a09ff41c9e94_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_1_of_4.py b/api/src/pcapi/alembic/versions/20241226T163901_a09ff41c9e94_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_1_of_4.py new file mode 100644 index 00000000000..2f1160df72e --- /dev/null +++ b/api/src/pcapi/alembic/versions/20241226T163901_a09ff41c9e94_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_1_of_4.py @@ -0,0 +1,33 @@ +""" +Add NOT NULL constraint on "collective_stock.startDatetime" and "collective_stock.endDatetime" (step 1 of 4) +""" + +from alembic import op + + +# pre/post deployment: post +# revision identifiers, used by Alembic. +revision = "a09ff41c9e94" +down_revision = "b18478ab2ea8" +branch_labels: tuple[str] | None = None +depends_on: list[str] | None = None + + +def upgrade() -> None: + op.execute( + """ + ALTER TABLE "collective_stock" DROP CONSTRAINT IF EXISTS "collective_stock_startDatetime_not_null_constraint"; + ALTER TABLE "collective_stock" ADD CONSTRAINT "collective_stock_startDatetime_not_null_constraint" CHECK ("startDatetime" IS NOT NULL) NOT VALID; + """ + ) + op.execute( + """ + ALTER TABLE "collective_stock" DROP CONSTRAINT IF EXISTS "collective_stock_endDatetime_not_null_constraint"; + ALTER TABLE "collective_stock" ADD CONSTRAINT "collective_stock_endDatetime_not_null_constraint" CHECK ("endDatetime" IS NOT NULL) NOT VALID; + """ + ) + + +def downgrade() -> None: + op.drop_constraint("collective_stock_startDatetime_not_null_constraint", table_name="collective_stock") + op.drop_constraint("collective_stock_endDatetime_not_null_constraint", table_name="collective_stock") diff --git a/api/src/pcapi/alembic/versions/20241226T163901_a8d2c442cc67_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_4_of_4.py b/api/src/pcapi/alembic/versions/20241226T163901_a8d2c442cc67_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_4_of_4.py new file mode 100644 index 00000000000..7124ef8edb8 --- /dev/null +++ b/api/src/pcapi/alembic/versions/20241226T163901_a8d2c442cc67_add_not_null_constraint_on_collective_stock_startDatetime_endDatetime_step_4_of_4.py @@ -0,0 +1,27 @@ +""" +Add NOT NULL constraint on "collective_stock.startDatetime" and "collective_stock.endDatetime" (step 4 of 4) +""" + +from alembic import op + + +# pre/post deployment: post +# revision identifiers, used by Alembic. +revision = "a8d2c442cc67" +down_revision = "4302137d444a" +branch_labels: tuple[str] | None = None +depends_on: list[str] | None = None + + +def upgrade() -> None: + op.drop_constraint("collective_stock_startDatetime_not_null_constraint", table_name="collective_stock") + op.drop_constraint("collective_stock_endDatetime_not_null_constraint", table_name="collective_stock") + + +def downgrade() -> None: + op.execute( + """ALTER TABLE "collective_stock" ADD CONSTRAINT "collective_stock_startDatetime_not_null_constraint" CHECK ("startDatetime" IS NOT NULL) NOT VALID""" + ) + op.execute( + """ALTER TABLE "collective_stock" ADD CONSTRAINT "collective_stock_endDatetime_not_null_constraint" CHECK ("endDatetime" IS NOT NULL) NOT VALID""" + ) diff --git a/api/src/pcapi/core/educational/models.py b/api/src/pcapi/core/educational/models.py index 4a7cfd90471..8001098e08f 100644 --- a/api/src/pcapi/core/educational/models.py +++ b/api/src/pcapi/core/educational/models.py @@ -1421,8 +1421,8 @@ class CollectiveStock(PcObject, Base, Model): beginningDatetime: datetime = sa.Column(sa.DateTime, index=True, nullable=False) - startDatetime: datetime = sa.Column(sa.DateTime, nullable=True) - endDatetime: datetime = sa.Column(sa.DateTime, nullable=True) + startDatetime: datetime = sa.Column(sa.DateTime, nullable=False) + endDatetime: datetime = sa.Column(sa.DateTime, nullable=False) collectiveOfferId: int = sa.Column( sa.BigInteger, sa.ForeignKey("collective_offer.id"), index=True, nullable=False, unique=True diff --git a/api/src/pcapi/routes/serialization/collective_bookings_serialize.py b/api/src/pcapi/routes/serialization/collective_bookings_serialize.py index 8950abe77ca..b75a77d3b7d 100644 --- a/api/src/pcapi/routes/serialization/collective_bookings_serialize.py +++ b/api/src/pcapi/routes/serialization/collective_bookings_serialize.py @@ -77,11 +77,11 @@ class CollectiveBookingCollectiveStockResponseModel(BaseModel): offer_id: int event_beginning_datetime: str event_start_datetime: str - event_end_datetime: str | None + event_end_datetime: str offer_isbn: str | None offer_is_educational: bool number_of_tickets: int - booking_limit_datetime: str | None + booking_limit_datetime: str class Config: alias_generator = to_camel @@ -214,7 +214,7 @@ def _serialize_collective_booking_status_info( def serialize_collective_booking_stock( collective_booking: models.CollectiveBooking, ) -> CollectiveBookingCollectiveStockResponseModel: - return CollectiveBookingCollectiveStockResponseModel( # type: ignore[call-arg] + return CollectiveBookingCollectiveStockResponseModel( offerName=collective_booking.collectiveStock.collectiveOffer.name, offerId=collective_booking.collectiveStock.collectiveOfferId, eventBeginningDatetime=typing.cast( diff --git a/pro/src/apiClient/v1/models/CollectiveBookingCollectiveStockResponseModel.ts b/pro/src/apiClient/v1/models/CollectiveBookingCollectiveStockResponseModel.ts index eea4ac301db..996a64fb194 100644 --- a/pro/src/apiClient/v1/models/CollectiveBookingCollectiveStockResponseModel.ts +++ b/pro/src/apiClient/v1/models/CollectiveBookingCollectiveStockResponseModel.ts @@ -3,9 +3,9 @@ /* tslint:disable */ /* eslint-disable */ export type CollectiveBookingCollectiveStockResponseModel = { - bookingLimitDatetime?: string | null; + bookingLimitDatetime: string; eventBeginningDatetime: string; - eventEndDatetime?: string | null; + eventEndDatetime: string; eventStartDatetime: string; numberOfTickets: number; offerId: number; diff --git a/pro/src/commons/utils/factories/collectiveApiFactories.ts b/pro/src/commons/utils/factories/collectiveApiFactories.ts index 12a28a1a016..e3b73eb2ace 100644 --- a/pro/src/commons/utils/factories/collectiveApiFactories.ts +++ b/pro/src/commons/utils/factories/collectiveApiFactories.ts @@ -186,6 +186,7 @@ export const collectiveBookingCollectiveStockFactory = ( bookingLimitDatetime: new Date().toISOString(), eventBeginningDatetime: new Date().toISOString(), eventStartDatetime: new Date().toISOString(), + eventEndDatetime: new Date().toISOString(), numberOfTickets: 1, offerId: 1, offerIsEducational: true, diff --git a/pro/src/components/Bookings/BookingsRecapTable/BookingsTable/Cells/__specs__/BookingOfferCell.spec.tsx b/pro/src/components/Bookings/BookingsRecapTable/BookingsTable/Cells/__specs__/BookingOfferCell.spec.tsx index eb5359fd7a9..140a79dd4d4 100644 --- a/pro/src/components/Bookings/BookingsRecapTable/BookingsTable/Cells/__specs__/BookingOfferCell.spec.tsx +++ b/pro/src/components/Bookings/BookingsRecapTable/BookingsTable/Cells/__specs__/BookingOfferCell.spec.tsx @@ -20,8 +20,10 @@ describe('bookings offer cell', () => { offerIsbn: '97834567654', offerName: 'La Guitare pour les nuls', offerIsEducational: false, + bookingLimitDatetime: new Date().toISOString(), eventBeginningDatetime: new Date().toISOString(), eventStartDatetime: new Date().toISOString(), + eventEndDatetime: new Date().toISOString(), numberOfTickets: 1, }, }), @@ -43,8 +45,10 @@ describe('bookings offer cell', () => { offerId: offerId, offerName: 'Guitare acoustique', offerIsEducational: false, + bookingLimitDatetime: new Date().toISOString(), eventBeginningDatetime: new Date().toISOString(), eventStartDatetime: new Date().toISOString(), + eventEndDatetime: new Date().toISOString(), numberOfTickets: 1, }, }), @@ -61,8 +65,10 @@ describe('bookings offer cell', () => { const props: BookingOfferCellProps = { booking: collectiveBookingFactory({ stock: { + bookingLimitDatetime: '2020-05-12T11:03:28.564687+04:00', eventBeginningDatetime: '2020-05-12T11:03:28.564687+04:00', eventStartDatetime: new Date().toISOString(), + eventEndDatetime: new Date().toISOString(), offerId: offerId, offerName: 'La danse des poireaux', offerIsEducational: false, @@ -89,6 +95,7 @@ describe('bookings offer cell', () => { bookingLimitDatetime: tomorrowFns.toISOString(), eventBeginningDatetime: new Date().toISOString(), eventStartDatetime: new Date().toISOString(), + eventEndDatetime: new Date().toISOString(), numberOfTickets: 1, offerId: offerId, offerIsEducational: true, @@ -114,6 +121,7 @@ describe('bookings offer cell', () => { bookingLimitDatetime: eightDaysFns.toISOString(), eventBeginningDatetime: new Date().toISOString(), eventStartDatetime: new Date().toISOString(), + eventEndDatetime: new Date().toISOString(), numberOfTickets: 1, offerId: offerId, offerIsEducational: false,