Skip to content

Commit c0c6b83

Browse files
Merge pull request #7241 from hotosm/staging
v5.5.3 to production
2 parents 856aa08 + 182621d commit c0c6b83

42 files changed

Lines changed: 817 additions & 346 deletions

Some content is hidden

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

.github/labeler.yml

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
11
"scope: frontend":
2-
- frontend/**/*
2+
- changed-files:
3+
- any-glob-to-any-file:
4+
- frontend/**/*
5+
36
"scope: backend":
4-
- backend/**/*
5-
- tests/**/*
6-
- migrations/**/*
7-
- ./manage.py
8-
- ./pyproject.toml
7+
- changed-files:
8+
- any-glob-to-any-file:
9+
- backend/**/*
10+
- tests/**/*
11+
- migrations/**/*
12+
- manage.py
13+
- pyproject.toml
14+
915
"scope: infrastructure":
10-
- .circleci/*
11-
- .github/**/*
12-
- scripts/aws/**/*
13-
- scripts/docker/**/*
14-
- scripts/install/**/*
15-
- docker-*.yml
16+
- changed-files:
17+
- any-glob-to-any-file:
18+
- .circleci/*
19+
- .github/**/*
20+
- scripts/aws/**/*
21+
- scripts/docker/**/*
22+
- scripts/install/**/*
23+
- docker-*.yml
24+
1625
"dependencies":
17-
- frontend/yarn.lock
18-
- ./pyproject.toml
26+
- changed-files:
27+
- any-glob-to-any-file:
28+
- frontend/yarn.lock
29+
- pyproject.toml
30+
1931
"scope: translations":
20-
- frontend/src/locales/*
32+
- changed-files:
33+
- any-glob-to-any-file:
34+
- frontend/src/locales/*

.github/workflows/backend-build-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ jobs:
4848
fail-fast: false
4949

5050
steps:
51-
- uses: actions/checkout@v4
51+
- uses: actions/checkout@v6
5252

5353
- name: Configure AWS credentials
54-
uses: aws-actions/configure-aws-credentials@v4
54+
uses: aws-actions/configure-aws-credentials@v6
5555
with:
5656
aws-region: ${{ env.AWS_REGION }}
5757
role-to-assume: ${{ env.OIDC_ROLE_ARN }}

.github/workflows/frontend-build-deploy.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ jobs:
2121

2222
steps:
2323
- name: Clone repository
24-
uses: actions/checkout@v4
24+
uses: actions/checkout@v6
2525

2626
- name: Use Node.js 22.11.0
27-
uses: actions/setup-node@v4
27+
uses: actions/setup-node@v6
2828
with:
2929
node-version: 22.11.0
3030

3131
- name: Cache node_modules
32-
uses: actions/cache@v4
32+
uses: actions/cache@v5
3333
with:
3434
path: frontend/node_modules
3535
key: tm-fe-${{ runner.os }}-build-${{ hashFiles('frontend/package.json') }}
@@ -62,7 +62,7 @@ jobs:
6262
yarn build
6363
6464
- name: Upload Builds Artifacts
65-
uses: actions/upload-artifact@v4
65+
uses: actions/upload-artifact@v7
6666
with:
6767
name: tm-fe-${{ github.sha }}
6868
path: ./frontend/build
@@ -84,10 +84,10 @@ jobs:
8484

8585
steps:
8686
- name: Clone repository
87-
uses: actions/checkout@v4
87+
uses: actions/checkout@v6
8888

8989
- name: Download Build Artifacts
90-
uses: actions/download-artifact@v4
90+
uses: actions/download-artifact@v7
9191
with:
9292
name: tm-fe-${{ github.sha }}
9393
path: ./build

.github/workflows/pr_label.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ jobs:
1515
runs-on: ubuntu-latest
1616

1717
steps:
18-
- uses: actions/labeler@v3
18+
- uses: actions/labeler@v6
1919
with:
2020
repo-token: "${{ secrets.GITHUB_TOKEN }}"

.github/workflows/pr_test_backend.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- name: Clone repository
19-
uses: actions/checkout@v4
19+
uses: actions/checkout@v6
2020

2121
- name: Set up Python 3.10
22-
uses: actions/setup-python@v4
22+
uses: actions/setup-python@v6
2323
with:
2424
python-version: "3.10"
2525

.github/workflows/pr_test_frontend.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ jobs:
2121

2222
steps:
2323
- name: Clone repository
24-
uses: actions/checkout@v4
24+
uses: actions/checkout@v6
2525

2626
- name: Use Node.js 22.11.0
27-
uses: actions/setup-node@v4
27+
uses: actions/setup-node@v6
2828
with:
2929
node-version: 22.11.0
3030

3131
- name: Cache node_modules
32-
uses: actions/cache@v4
32+
uses: actions/cache@v5
3333
with:
3434
path: frontend/node_modules
3535
key: tm-fe-${{ runner.os }}-build-${{ hashFiles('frontend/package.json') }}
@@ -66,7 +66,7 @@ jobs:
6666
yarn build
6767
6868
- name: Upload Builds Artifacts
69-
uses: actions/upload-artifact@v4
69+
uses: actions/upload-artifact@v7
7070
with:
7171
name: tm-fe-${{ github.sha }}
7272
path: ./frontend/build

backend/api/users/statistics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ async def get_ohsome_stats(
176176
db: Database = Depends(get_db),
177177
userId: int = Query(..., description="OSM user ID"),
178178
topics: str = Query(
179-
..., description="Comma-separated list of OSM topics, e.g. building,highway"
179+
..., description="Comma-separated list of OSM topics, e.g. building,road"
180180
),
181181
startdate: Optional[str] = Query(
182182
None, description="YYYY-MM-DD, start of time range"

backend/models/postgis/project.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,10 +926,15 @@ async def delete(self, db: Database):
926926
"project_partnerships",
927927
]
928928

929+
# Whitelist of allowed table names to prevent SQL injection via
930+
# dynamic table name interpolation, since table names cannot be parameterized.
931+
allowed_tables = set(related_tables)
932+
929933
# Start a transaction to ensure atomic deletion
930934
async with db.transaction():
931-
# Loop through each table and execute the delete query
932935
for table in related_tables:
936+
if table not in allowed_tables:
937+
raise ValueError(f"Invalid table name: {table}")
933938
await db.execute(
934939
f"DELETE FROM {table} WHERE project_id = :project_id",
935940
{"project_id": self.id},

backend/models/postgis/task.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,22 @@ async def update_task_locked_with_duration(
383383
try:
384384
# Fetch the last locked task history entry with raw SQL
385385
query = """
386-
SELECT id, action_date
387-
FROM task_history
388-
WHERE task_id = :task_id
389-
AND project_id = :project_id
390-
AND action = :action
391-
AND action_text IS NULL
392-
AND user_id = :user_id
393-
ORDER BY action_date DESC
394-
LIMIT 1
386+
WITH locked_rows AS (
387+
SELECT
388+
id,
389+
action_date,
390+
COUNT(*) OVER() AS total_rows
391+
FROM task_history
392+
WHERE task_id = :task_id
393+
AND project_id = :project_id
394+
AND action = :action
395+
AND action_text IS NULL
396+
AND user_id = :user_id
397+
ORDER BY action_date DESC, id DESC
398+
FETCH FIRST 1 ROW ONLY
399+
)
400+
SELECT id, action_date, total_rows
401+
FROM locked_rows
395402
"""
396403
values = {
397404
"task_id": task_id,
@@ -410,6 +417,9 @@ async def update_task_locked_with_duration(
410417
# No record found, possibly a race condition or auto-unlock scenario.
411418
return
412419

420+
if last_locked["total_rows"] > 1:
421+
raise MultipleResultsFound()
422+
413423
# Calculate the duration the task was locked for
414424
duration_task_locked = (
415425
datetime.datetime.utcnow() - last_locked["action_date"]
@@ -1183,6 +1193,14 @@ async def unlock_task(
11831193
):
11841194
"""Unlock the task and change its state."""
11851195
# Add task comment history if provided
1196+
if not undo:
1197+
last_history = await TaskHistory.get_last_action(project_id, task_id, db)
1198+
# To unlock a task the last action must have been either lock or extension
1199+
last_action = TaskAction[last_history.action]
1200+
await TaskHistory.update_task_locked_with_duration(
1201+
task_id, project_id, last_action, user_id, db
1202+
)
1203+
11861204
if comment:
11871205
await Task.set_task_history(
11881206
task_id,
@@ -1283,12 +1301,6 @@ async def unlock_task(
12831301
"project_id": project_id,
12841302
},
12851303
)
1286-
1287-
# Update task locked duration in the history when `undo` is False
1288-
# Using a slightly evil side effect of Actions and Statuses having the same name here :)
1289-
await TaskHistory.update_task_locked_with_duration(
1290-
task_id, project_id, TaskStatus(current_status), user_id, db
1291-
)
12921304
# Final query for updating task status
12931305
final_update_query = """
12941306
UPDATE tasks
@@ -1333,10 +1345,12 @@ async def reset_lock(
13331345
db=db,
13341346
)
13351347
# Update task lock history with duration
1348+
last_history = await TaskHistory.get_last_action(project_id, task_id, db)
1349+
last_action = TaskAction[last_history.action]
13361350
await TaskHistory.update_task_locked_with_duration(
13371351
task_id=task_id,
13381352
project_id=project_id,
1339-
lock_action=TaskStatus(task_status),
1353+
lock_action=last_action,
13401354
user_id=user_id,
13411355
db=db,
13421356
)

backend/services/mapping_service.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,6 @@ async def lock_time_can_be_extended(
544544
raise NotFound(
545545
sub_code="TASK_NOT_FOUND", project_id=project_id, task_id=task_id
546546
)
547-
548547
if TaskStatus(task.task_status) not in [
549548
TaskStatus.LOCKED_FOR_MAPPING,
550549
TaskStatus.LOCKED_FOR_VALIDATION,
@@ -574,14 +573,18 @@ async def extend_task_lock_time(extend_dto: ExtendLockTimeDTO, db: Database):
574573
task = await Task.get(task_id, extend_dto.project_id, db)
575574
action = (
576575
TaskAction.EXTENDED_FOR_MAPPING
577-
if task["task_status"] == TaskStatus.LOCKED_FOR_MAPPING
576+
if TaskStatus(task.task_status) == TaskStatus.LOCKED_FOR_MAPPING
578577
else TaskAction.EXTENDED_FOR_VALIDATION
579578
)
580579

580+
last_history = await TaskHistory.get_last_action(
581+
extend_dto.project_id, task_id, db
582+
)
583+
last_action = TaskAction[last_history.action]
581584
await TaskHistory.update_task_locked_with_duration(
582585
task_id,
583586
extend_dto.project_id,
584-
TaskStatus(task["task_status"]),
587+
last_action,
585588
extend_dto.user_id,
586589
db,
587590
)

0 commit comments

Comments
 (0)