Skip to content

Commit 1f9bf7e

Browse files
authored
fix: Fixed route update mechanism (#208)
* refactor: Made device_id and media_id madnatory in the alert table * refactor: Refactored schemas * docs: Added stop-dev command to Makefile * feat: Added DEBUG to each docker compose and restart * refactor: Deprecated specified_only in update_entry * refactor: Deprecated update routes of alert table * refactor: Reflected schemas refacto * test: Fixed unittests * test: Fixed unittests * refactor: Deprecated media update route * feat: Enforced strict update * refactor: Reflected changes from API * test: Updated unittests accordingly * style: Fixed style * test: Fixed unittests * fix: Fixed validators * test: Updated unittest * test: Removed debug code * test: Added unittest for webhooks * test: Fixed webhook unittests
1 parent 5f719ff commit 1f9bf7e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1217
-599
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ run-dev:
3333
docker build src/. -t pyroapi:python3.8-alpine3.10
3434
docker-compose -f docker-compose-dev.yml up -d --build
3535

36+
stop-dev:
37+
docker-compose -f docker-compose-dev.yml down
38+
3639
# Run tests for the library
3740
test:
3841
docker build src/. -t pyroapi:python3.8-alpine3.10

client/pyroclient/client.py

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ def heartbeat(self) -> Response:
116116

117117
def update_my_location(
118118
self,
119-
lat: Union[float, None] = None,
120-
lon: Union[float, None] = None,
121-
elevation: Union[float, None] = None,
122-
yaw: Union[float, None] = None,
123-
pitch: Union[float, None] = None,
119+
lat: float,
120+
lon: float,
121+
elevation: float,
122+
yaw: float,
123+
pitch: float,
124124
) -> Response:
125125
"""Updates the location of the device
126126
@@ -132,22 +132,7 @@ def update_my_location(
132132
Returns:
133133
HTTP response containing the update device info
134134
"""
135-
payload = {}
136-
137-
if lat is not None:
138-
payload["lat"] = lat
139-
if lon is not None:
140-
payload["lon"] = lon
141-
if elevation is not None:
142-
payload["elevation"] = elevation
143-
if yaw is not None:
144-
payload["yaw"] = yaw
145-
if pitch is not None:
146-
payload["pitch"] = pitch
147-
148-
if len(payload) == 0:
149-
raise ValueError("At least one location information" + "(lat, lon, elevation, yaw, pitch) must be filled")
150-
135+
payload = {"lat": lat, "lon": lon, "elevation": elevation, "yaw": yaw, "pitch": pitch}
151136
return requests.put(self.routes["update-my-location"], headers=self.headers, json=payload)
152137

153138
def create_event(self, lat: float, lon: float) -> Response:
@@ -198,9 +183,9 @@ def send_alert(
198183
lat: float,
199184
lon: float,
200185
device_id: int,
186+
media_id: int,
201187
azimuth: Union[float, None] = None,
202188
event_id: Union[int, None] = None,
203-
media_id: Union[int, None] = None,
204189
) -> Response:
205190
"""Raise an alert to the API.
206191
@@ -212,10 +197,10 @@ def send_alert(
212197
Args:
213198
lat: the latitude of the alert
214199
lon: the longitude of the alert
200+
device_id: ID of the device that sent this alert
201+
media_id: media ID linked to this alert
215202
azimuth: the azimuth of the alert
216203
event_id: the ID of the event this alerts relates to
217-
device_id: the ID of the device that raised this alert
218-
media_id: optional media ID linked to this alert
219204
220205
Returns:
221206
HTTP response containing the created alert
@@ -232,9 +217,9 @@ def send_alert_from_device(
232217
self,
233218
lat: float,
234219
lon: float,
220+
media_id: int,
235221
azimuth: Union[float, None] = None,
236222
event_id: Union[int, None] = None,
237-
media_id: Union[int, None] = None,
238223
) -> Response:
239224
"""Raise an alert to the API from a device (no need to specify device ID).
240225
@@ -246,9 +231,9 @@ def send_alert_from_device(
246231
Args:
247232
lat: the latitude of the alert
248233
lon: the longitude of the alert
234+
media_id: media ID linked to this alert
249235
azimuth: the azimuth of the alert
250236
event_id: the ID of the event this alerts relates to
251-
media_id: optional media ID linked to this alert
252237
253238
Returns:
254239
HTTP response containing the created alert

docker-compose-dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ services:
55
build:
66
context: src
77
dockerfile: Dockerfile-dev
8+
restart: always
89
command: uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 8080
910
volumes:
1011
- ./src/:/app/
1112
ports:
1213
- 8080:8080
1314
environment:
15+
- DEBUG=true
1416
- DATABASE_URL=postgresql://dummy_pg_user:dummy_pg_pwd@db/dummy_pg_db
1517
- TEST_DATABASE_URL=postgresql://dummy_pg_tuser:dummy_pg_tpwd@db_test/dummy_pg_tdb
1618
- SUPERUSER_LOGIN=dummy_login

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ version: '3.7'
33
services:
44
pyroapi:
55
build: src
6+
restart: always
67
command: uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 8080
78
volumes:
89
- ./src/:/app/
910
ports:
1011
- 8080:8080
1112
environment:
13+
- DEBUG=false
1214
- DATABASE_URL=postgresql://dummy_pg_user:dummy_pg_pwd@db/dummy_pg_db
1315
- SUPERUSER_LOGIN=dummy_login
1416
- SUPERUSER_PWD=dummy_pwd

src/app/api/crud/accesses.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async def create_accessed_entry(
7777
return entry
7878

7979

80-
async def update_accessed_entry(table: Table, accesses: Table, entry_id: int, payload: Any, only_specified=True):
80+
async def update_accessed_entry(table: Table, accesses: Table, entry_id: int, payload: Any):
8181
"""Update an entry with a special treatment regarding login: if login is set -> update corresponding access."""
8282
# Ensure database consistency between tables with a transaction (login must remain the same in table & accesses)
8383
async with base.database.transaction():
@@ -94,7 +94,7 @@ async def update_accessed_entry(table: Table, accesses: Table, entry_id: int, pa
9494
await update_login(accesses, payload.login, origin_entry["access_id"])
9595

9696
# Update entry with input payload
97-
entry = await base.update_entry(table, payload, entry_id, only_specified)
97+
entry = await base.update_entry(table, payload, entry_id)
9898

9999
return entry
100100

src/app/api/crud/authorizations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from sqlalchemy import Table
88

99
from app.api import crud
10-
from app.api.schemas import AccessType
1110
from app.db import accesses
11+
from app.db.models import AccessType
1212

1313

1414
async def is_in_same_group(table: Table, entry_id: int, group_id: int) -> bool:

src/app/api/crud/base.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,27 +84,17 @@ async def get_entry(table: Table, entry_id: int = Path(..., gt=0)) -> Dict[str,
8484
return dict(entry)
8585

8686

87-
async def update_entry(
88-
table: Table, payload: BaseModel, entry_id: int = Path(..., gt=0), only_specified: bool = True
89-
) -> Dict[str, Any]:
87+
async def update_entry(table: Table, payload: BaseModel, entry_id: int = Path(..., gt=0)) -> Dict[str, Any]:
9088
payload_dict = payload.dict()
9189

92-
if only_specified:
93-
# Dont update columns for null fields
94-
payload_dict = {k: v for k, v in payload_dict.items() if v is not None}
95-
9690
_id = await put(entry_id, payload_dict, table)
9791

9892
if not isinstance(_id, int):
9993
raise HTTPException(
10094
status_code=status.HTTP_404_NOT_FOUND, detail=f"Table {table.name} has no entry with id={entry_id}"
10195
)
10296

103-
if only_specified:
104-
# Retrieve complete record values
105-
return dict(await get(entry_id, table))
106-
else:
107-
return {**payload.dict(), "id": entry_id}
97+
return {**payload.dict(), "id": entry_id}
10898

10999

110100
async def delete_entry(table: Table, entry_id: int = Path(..., gt=0)) -> Dict[str, Any]:

src/app/api/deps.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
import app.config as cfg
1212
from app.api import crud
13-
from app.api.schemas import AccessRead, AccessType, DeviceOut, TokenPayload, UserRead
13+
from app.api.schemas import AccessRead, DeviceOut, TokenPayload, UserRead
1414
from app.db import accesses, devices, users
15+
from app.db.models import AccessType
1516

1617
# Scope definition
1718
oauth2_scheme = OAuth2PasswordBearer(

src/app/api/routes/accesses.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
from app.api import crud
1111
from app.api.deps import get_current_access
12-
from app.api.schemas import AccessRead, AccessType
12+
from app.api.schemas.accesses import AccessRead
1313
from app.db import accesses
14+
from app.db.models import AccessType
1415

1516
router = APIRouter()
1617

src/app/api/routes/alerts.py

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
from sqlalchemy import select
1111

1212
from app.api import crud
13-
from app.api.crud.authorizations import check_group_read, check_group_update, is_admin_access
13+
from app.api.crud.authorizations import check_group_read, is_admin_access
1414
from app.api.crud.groups import get_entity_group_id
1515
from app.api.deps import get_current_access, get_current_device
1616
from app.api.external import post_request
17-
from app.api.schemas import AccessType, AlertBase, AlertIn, AlertMediaId, AlertOut, DeviceOut
17+
from app.api.schemas import AlertBase, AlertIn, AlertOut, DeviceOut
1818
from app.db import alerts, events, get_session, media, models
19+
from app.db.models import AccessType
1920

2021
router = APIRouter()
2122

@@ -110,20 +111,6 @@ async def fetch_alerts(
110111
return retrieved_alerts
111112

112113

113-
@router.put("/{alert_id}/", response_model=AlertOut, summary="Update information about a specific alert")
114-
async def update_alert(
115-
payload: AlertIn,
116-
alert_id: int = Path(..., gt=0),
117-
requester=Security(get_current_access, scopes=[AccessType.admin, AccessType.user]),
118-
):
119-
"""
120-
Based on a alert_id, updates information about the specified alert
121-
"""
122-
requested_group_id = await get_entity_group_id(alerts, alert_id)
123-
await check_group_update(requester.id, requested_group_id)
124-
return await crud.update_entry(alerts, payload, alert_id)
125-
126-
127114
@router.delete("/{alert_id}/", response_model=AlertOut, summary="Delete a specific alert")
128115
async def delete_alert(alert_id: int = Path(..., gt=0), _=Security(get_current_access, scopes=[AccessType.admin])):
129116
"""
@@ -132,29 +119,6 @@ async def delete_alert(alert_id: int = Path(..., gt=0), _=Security(get_current_a
132119
return await crud.delete_entry(alerts, alert_id)
133120

134121

135-
@router.put("/{alert_id}/link-media", response_model=AlertOut, summary="Link an alert to a media")
136-
async def link_media(
137-
payload: AlertMediaId,
138-
alert_id: int = Path(..., gt=0),
139-
current_device: DeviceOut = Security(get_current_device, scopes=[AccessType.device]),
140-
):
141-
"""
142-
Based on a alert_id, and media information as arguments, link the specified alert to a media
143-
"""
144-
# Check that alert is linked to this device
145-
existing_alert = await crud.fetch_one(alerts, {"id": alert_id, "device_id": current_device.id})
146-
if existing_alert is None:
147-
raise HTTPException(
148-
status_code=status.HTTP_404_NOT_FOUND,
149-
detail=f"Unable to find alert with id={alert_id} & device_id={current_device.id}.",
150-
)
151-
152-
await check_media_existence(payload.media_id)
153-
existing_alert = dict(**existing_alert)
154-
existing_alert["media_id"] = payload.media_id
155-
return await crud.update_entry(alerts, AlertIn(**existing_alert), alert_id)
156-
157-
158122
@router.get("/ongoing", response_model=List[AlertOut], summary="Get the list of ongoing alerts")
159123
async def fetch_ongoing_alerts(
160124
requester=Security(get_current_access, scopes=[AccessType.admin, AccessType.user]), session=Depends(get_session)

0 commit comments

Comments
 (0)