From 3dae25d4e9dacead56078f733f32a2627fd0fcfd Mon Sep 17 00:00:00 2001 From: 99cloud Date: Mon, 30 Jan 2023 13:46:28 +0800 Subject: [PATCH] fix: restrict intection delete --- apitest/edge_node_rsu.yaml | 2 ++ .../versions/5a9e7ac60afb_rse_intersection.py | 20 +++++++++++-------- .../versions/a04def91cc98_intersection_fk.py | 20 +++++++++++-------- .../api/api_v1/endpoints/intersection.py | 16 +++++++++++++-- dandelion/crud/crud_intersection.py | 14 ++++++++++++- dandelion/models/camera.py | 6 +++++- dandelion/models/edge_rsu.py | 6 +++++- dandelion/models/lidar.py | 6 +++++- dandelion/models/map.py | 6 +++++- dandelion/models/radar.py | 6 +++++- dandelion/models/rsi_event.py | 6 +++++- dandelion/models/rsu.py | 6 +++++- dandelion/models/spat.py | 6 +++++- dandelion/schemas/intersection.py | 1 + tools/datainit.py | 2 +- 15 files changed, 95 insertions(+), 28 deletions(-) diff --git a/apitest/edge_node_rsu.yaml b/apitest/edge_node_rsu.yaml index 1de2ba5..c9f8672 100644 --- a/apitest/edge_node_rsu.yaml +++ b/apitest/edge_node_rsu.yaml @@ -54,6 +54,7 @@ tests: esn: "RSU_ESN02" areaCode: '320115' location: {} + intersectionCode: "32011501" response_json_paths: $.name: 'RSU_NAME_02' $.esn: "RSU_ESN02" @@ -69,6 +70,7 @@ tests: esn: "RSU_ESN03" areaCode: '320115' location: { } + intersectionCode: "32011501" response_json_paths: $.name: 'RSU_NAME_03' $.esn: "RSU_ESN03" diff --git a/dandelion/alembic/versions/5a9e7ac60afb_rse_intersection.py b/dandelion/alembic/versions/5a9e7ac60afb_rse_intersection.py index 6b1e50b..2d57f32 100644 --- a/dandelion/alembic/versions/5a9e7ac60afb_rse_intersection.py +++ b/dandelion/alembic/versions/5a9e7ac60afb_rse_intersection.py @@ -34,21 +34,25 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.add_column("camera", sa.Column("intersection_code", sa.String(length=64), nullable=True)) + op.add_column("camera", sa.Column("intersection_code", sa.String(length=64), nullable=False)) op.create_foreign_key( - "camera_fk_intersection_code", "camera", "intersection", ["intersection_code"], ["code"] + "camera_fk_intersection_code", "camera", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) - op.add_column("radar", sa.Column("intersection_code", sa.String(length=64), nullable=True)) + op.add_column("radar", sa.Column("intersection_code", sa.String(length=64), nullable=False)) op.create_foreign_key( - "radar_fk_intersection_code", "radar", "intersection", ["intersection_code"], ["code"] + "radar_fk_intersection_code", "radar", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) - op.add_column("spat", sa.Column("intersection_code", sa.String(length=64), nullable=True)) + op.add_column("spat", sa.Column("intersection_code", sa.String(length=64), nullable=False)) op.create_foreign_key( - "spat_fk_intersection_code", "spat", "intersection", ["intersection_code"], ["code"] + "spat_fk_intersection_code", "spat", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) - op.add_column("lidar", sa.Column("intersection_code", sa.String(length=64), nullable=True)) + op.add_column("lidar", sa.Column("intersection_code", sa.String(length=64), nullable=False)) op.create_foreign_key( - "lidar_fk_intersection_code", "lidar", "intersection", ["intersection_code"], ["code"] + "lidar_fk_intersection_code", "lidar", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) # ### end Alembic commands ### diff --git a/dandelion/alembic/versions/a04def91cc98_intersection_fk.py b/dandelion/alembic/versions/a04def91cc98_intersection_fk.py index f9d3689..5b9e098 100644 --- a/dandelion/alembic/versions/a04def91cc98_intersection_fk.py +++ b/dandelion/alembic/versions/a04def91cc98_intersection_fk.py @@ -58,37 +58,41 @@ def upgrade(): with op.batch_alter_table("map", schema=None) as batch_op: batch_op.add_column( # type: ignore[attr-defined] - sa.Column("intersection_code", sa.String(length=64), nullable=True)) + sa.Column("intersection_code", sa.String(length=64), nullable=False)) batch_op.drop_column("address") # type: ignore[attr-defined] batch_op.drop_column("area_code") # type: ignore[attr-defined] batch_op.create_foreign_key( # type: ignore[attr-defined] - "intersection_fk_code_map", "intersection", ["intersection_code"], ["code"] + "intersection_fk_code_map", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) with op.batch_alter_table("rsi_event", schema=None) as batch_op: batch_op.add_column( # type: ignore[attr-defined] - sa.Column("intersection_code", sa.String(length=64), nullable=True)) + sa.Column("intersection_code", sa.String(length=64), nullable=False)) batch_op.drop_column("address") # type: ignore[attr-defined] batch_op.drop_column("area_code") # type: ignore[attr-defined] batch_op.create_foreign_key( # type: ignore[attr-defined] - "intersection_fk_code_rsi_event", "intersection", ["intersection_code"], ["code"] + "intersection_fk_code_rsi_event", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) with op.batch_alter_table("rsu", schema=None) as batch_op: batch_op.add_column( # type: ignore[attr-defined] - sa.Column("intersection_code", sa.String(length=64), nullable=True)) + sa.Column("intersection_code", sa.String(length=64), nullable=False)) batch_op.drop_column("address") # type: ignore[attr-defined] batch_op.drop_column("area_code") # type: ignore[attr-defined] batch_op.create_foreign_key( # type: ignore[attr-defined] - "intersection_fk_code_rsu", "intersection", ["intersection_code"], ["code"] + "intersection_fk_code_rsu", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) with op.batch_alter_table("edge_node_rsu", schema=None) as batch_op: batch_op.add_column( # type: ignore[attr-defined] - sa.Column("intersection_code", sa.String(length=64), nullable=True)) + sa.Column("intersection_code", sa.String(length=64), nullable=False)) batch_op.drop_column("area_code") # type: ignore[attr-defined] batch_op.create_foreign_key( # type: ignore[attr-defined] - "intersection_fk_code_edge_node_rsu", "intersection", ["intersection_code"], ["code"] + "intersection_fk_code_edge_node_rsu", "intersection", ["intersection_code"], ["code"], + onupdate='CASCADE', ondelete='RESTRICT' ) # mypy: end ignore # ### end Alembic commands ### diff --git a/dandelion/api/api_v1/endpoints/intersection.py b/dandelion/api/api_v1/endpoints/intersection.py index 1ae5102..af9eed0 100644 --- a/dandelion/api/api_v1/endpoints/intersection.py +++ b/dandelion/api/api_v1/endpoints/intersection.py @@ -55,7 +55,7 @@ def create( db: Session = Depends(deps.get_db), current_user: models.User = Depends(deps.get_current_user), ) -> schemas.Intersection: - if crud.intersection.get_by_code_and_area( + if crud.intersection.get_by_name_and_area( db=db, name=intersection_in.name, area_code=intersection_in.area_code ): raise HTTPException( @@ -101,6 +101,11 @@ def delete( try: crud.intersection.remove(db, id=intersection_id) except sql_exc.IntegrityError as ex: + if eval(re.findall(r"\(pymysql.err.IntegrityError\) (.*)", ex.args[0])[0])[0] == 1048: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Intersection [id: {intersection_id}] cannot delete", + ) raise error_handle(ex, "id", str(intersection_id)) return Response(content=None, status_code=status.HTTP_204_NO_CONTENT) @@ -182,7 +187,7 @@ def get_all( ) -@router.patch( +@router.put( "/{intersection_id}", response_model=schemas.Intersection, status_code=status.HTTP_200_OK, @@ -216,6 +221,13 @@ def update( area_code = ( intersection_in.area_code if intersection_in.area_code else intersection_in_db.area_code ) + if crud.intersection.get_by_code_and_id( + db=db, code=str(intersection_in.code), intersection_id=intersection_id + ): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Intersection [code: {intersection_in.code}] already exist", + ) try: new_intersection_in_db = crud.intersection.update( db, db_obj=intersection_in_db, obj_in=intersection_in diff --git a/dandelion/crud/crud_intersection.py b/dandelion/crud/crud_intersection.py index 008ff83..3767bf0 100644 --- a/dandelion/crud/crud_intersection.py +++ b/dandelion/crud/crud_intersection.py @@ -58,7 +58,7 @@ def get_multi_with_total( def get_by_code(self, db: Session, code: str) -> Optional[Intersection]: return db.query(self.model).filter(self.model.code == code).first() - def get_by_code_and_area( + def get_by_name_and_area( self, db: Session, name: str, @@ -70,5 +70,17 @@ def get_by_code_and_area( .first() ) + def get_by_code_and_id( + self, + db: Session, + code: str, + intersection_id: int, + ) -> Optional[Intersection]: + return ( + db.query(self.model) + .filter(self.model.code == code, self.model.id != intersection_id) + .first() + ) + intersection = CRUDIntersection(Intersection) diff --git a/dandelion/models/camera.py b/dandelion/models/camera.py index 8e01f5d..50e8117 100644 --- a/dandelion/models/camera.py +++ b/dandelion/models/camera.py @@ -35,7 +35,11 @@ class Camera(Base, DandelionBase): desc = Column(String(255), nullable=False, default="") enabled = Column(Boolean, nullable=True, default=True) - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) def __repr__(self) -> str: return f"" diff --git a/dandelion/models/edge_rsu.py b/dandelion/models/edge_rsu.py index 88952e7..8cac8be 100644 --- a/dandelion/models/edge_rsu.py +++ b/dandelion/models/edge_rsu.py @@ -23,7 +23,11 @@ class EdgeNodeRSU(Base, DandelionBase): __tablename__ = "edge_node_rsu" edge_node_id = Column(Integer, ForeignKey("edge_node.id")) - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) name = Column(String(64), nullable=False, index=True) esn = Column(String(64), nullable=False, index=True) location = Column(JSON, nullable=False) diff --git a/dandelion/models/lidar.py b/dandelion/models/lidar.py index 70fbefe..25c79eb 100644 --- a/dandelion/models/lidar.py +++ b/dandelion/models/lidar.py @@ -38,7 +38,11 @@ class Lidar(Base, DandelionBase): desc = Column(String(255), nullable=False, default="") ws_url = Column(String(50), nullable=False, default="") - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) def __repr__(self) -> str: return f"" diff --git a/dandelion/models/map.py b/dandelion/models/map.py index 4f7a4c5..418ac80 100644 --- a/dandelion/models/map.py +++ b/dandelion/models/map.py @@ -25,7 +25,11 @@ class Map(Base, DandelionBase): __tablename__ = "map" name = Column(String(64), nullable=False, index=True, unique=True) - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) desc = Column(String(255), nullable=False, default="") lat = Column(Float, nullable=False) lng = Column(Float, nullable=False) diff --git a/dandelion/models/radar.py b/dandelion/models/radar.py index a3f8a9c..c4a5d5a 100644 --- a/dandelion/models/radar.py +++ b/dandelion/models/radar.py @@ -35,7 +35,11 @@ class Radar(Base, DandelionBase): desc = Column(String(255), nullable=False, default="") enabled = Column(Boolean, nullable=True, default=True) - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) def __repr__(self) -> str: return f"" diff --git a/dandelion/models/rsi_event.py b/dandelion/models/rsi_event.py index fb7007b..8aaf041 100644 --- a/dandelion/models/rsi_event.py +++ b/dandelion/models/rsi_event.py @@ -47,7 +47,11 @@ class RSIEvent(Base, DandelionBase): __tablename__ = "rsi_event" rsu_id = Column(Integer, ForeignKey("rsu.id")) - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) alert_id = Column(String(64), nullable=True, default="") duration = Column(Integer, nullable=True) event_status = Column(Boolean, nullable=True, default=True) diff --git a/dandelion/models/rsu.py b/dandelion/models/rsu.py index bef0960..7fa5ad5 100644 --- a/dandelion/models/rsu.py +++ b/dandelion/models/rsu.py @@ -34,7 +34,11 @@ class RSU(Base, DandelionBase): config = Column(JSON, nullable=False) online_status = Column(Boolean, index=True, nullable=False, default=False) rsu_model_id = Column(Integer, ForeignKey("rsu_model.id")) - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) desc = Column(String(255), nullable=True, default="") log_id = Column(Integer, ForeignKey("rsu_log.id")) diff --git a/dandelion/models/spat.py b/dandelion/models/spat.py index 9be2529..6837d4e 100644 --- a/dandelion/models/spat.py +++ b/dandelion/models/spat.py @@ -37,7 +37,11 @@ class Spat(Base, DandelionBase): timing = Column(DateTime, nullable=False, default=lambda: datetime.utcnow()) desc = Column(String(255), nullable=False, default="") - intersection_code = Column(String(64), ForeignKey("intersection.code")) + intersection_code = Column( + String(64), + ForeignKey("intersection.code", onupdate="CASCADE", ondelete="RESTRICT"), + nullable=False, + ) __table_args__ = (UniqueConstraint("intersection_id", "phase_id"),) diff --git a/dandelion/schemas/intersection.py b/dandelion/schemas/intersection.py index 0c8dba5..fe9da01 100644 --- a/dandelion/schemas/intersection.py +++ b/dandelion/schemas/intersection.py @@ -37,6 +37,7 @@ class IntersectionCreate(IntersectionBase): class IntersectionUpdate(BaseModel): """""" + code: Optional[str] = Field(None, alias="code", description="Intersection code") name: Optional[str] = Field(None, alias="name", description="Intersection name") lat: Optional[str] = Field(None, alias="lat", description="Intersection latitude") lng: Optional[str] = Field(None, alias="lng", description="Intersection longitude") diff --git a/tools/datainit.py b/tools/datainit.py index e55c51c..0e1237d 100644 --- a/tools/datainit.py +++ b/tools/datainit.py @@ -126,7 +126,7 @@ def init_db() -> None: rsu1.version = "v1" rsu1.rsu_status = "Normal" rsu1.online_status = False - rsu1.location = {"lon": 118.8213963998263, "lat": 31.934846637757847} + rsu1.location = {"lon": 118.8213963998, "lat": 31.9348466377} rsu1.config = {} rsu1.rsu_model_id = rsu_model1.id rsu1.intersection_code = "32011501"