Skip to content

Commit 0a2e6e5

Browse files
pmcfadinclaude
andcommitted
fix: address review comments on user activity endpoint (closes #17)
- Use validation_alias instead of alias in UserActivityResponse so OpenAPI spec emits camelCase field names (userId, activityType, activityId, activityTimestamp) while model_validate() from snake_case DB objects still works - Change route path from {user_id_path:uuid} to {user_id_path} with UUID type annotation so invalid UUIDs produce 422 instead of 404 - Replace activity_type query param type str with Literal["view","comment","rate"] for enum validation and OpenAPI documentation - Replace max(1, math.ceil(...)) totalPages formula with integer division so empty result sets return totalPages=0 consistently - Fix test_get_user_activity_with_type_filter to assert item["activityType"] directly instead of hedging with .get() fallback - Regenerate docs/killrvideo_openapi.yaml to reflect all model/route changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a297bf3 commit 0a2e6e5

2 files changed

Lines changed: 156 additions & 2 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""API endpoint for querying user activity timelines."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Annotated, Literal, Optional
6+
from uuid import UUID
7+
8+
from fastapi import APIRouter, Depends, Query
9+
10+
from app.api.v1.dependencies import PaginationParams
11+
from app.models.common import PaginatedResponse, Pagination
12+
from app.models.user_activity import UserActivityResponse
13+
from app.services import user_activity_service
14+
15+
router = APIRouter(tags=["User Activity"])
16+
17+
18+
@router.get(
19+
"/users/{user_id_path}/activity",
20+
response_model=PaginatedResponse[UserActivityResponse],
21+
summary="Get user activity timeline",
22+
)
23+
async def get_user_activity(
24+
user_id_path: UUID,
25+
pagination: Annotated[PaginationParams, Depends()],
26+
activity_type: Optional[Literal["view", "comment", "rate"]] = Query(
27+
None, description="Filter by activity type (view, comment, rate)"
28+
),
29+
):
30+
"""Return a paginated timeline of a user's activity over the last 30 days."""
31+
32+
activities, total = await user_activity_service.list_user_activity(
33+
userid=user_id_path,
34+
page=pagination.page,
35+
page_size=pagination.pageSize,
36+
activity_type=activity_type,
37+
)
38+
39+
total_pages = (total + pagination.pageSize - 1) // pagination.pageSize
40+
41+
response_items = [UserActivityResponse.model_validate(a) for a in activities]
42+
43+
return PaginatedResponse[UserActivityResponse](
44+
data=response_items,
45+
pagination=Pagination(
46+
currentPage=pagination.page,
47+
pageSize=pagination.pageSize,
48+
totalItems=total,
49+
totalPages=total_pages,
50+
),
51+
)

docs/killrvideo_openapi.yaml

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,70 @@ paths:
12711271
application/json:
12721272
schema:
12731273
$ref: '#/components/schemas/HTTPValidationError'
1274+
/api/v1/users/{user_id_path}/activity:
1275+
get:
1276+
tags:
1277+
- User Activity
1278+
summary: Get user activity timeline
1279+
description: Return a paginated timeline of a user's activity over the last
1280+
30 days.
1281+
operationId: get_user_activity_api_v1_users__user_id_path__activity_get
1282+
parameters:
1283+
- name: user_id_path
1284+
in: path
1285+
required: true
1286+
schema:
1287+
type: string
1288+
format: uuid
1289+
title: User Id Path
1290+
- name: activity_type
1291+
in: query
1292+
required: false
1293+
schema:
1294+
anyOf:
1295+
- enum:
1296+
- view
1297+
- comment
1298+
- rate
1299+
type: string
1300+
- type: 'null'
1301+
description: Filter by activity type (view, comment, rate)
1302+
title: Activity Type
1303+
description: Filter by activity type (view, comment, rate)
1304+
- name: page
1305+
in: query
1306+
required: false
1307+
schema:
1308+
type: integer
1309+
minimum: 1
1310+
description: Page number
1311+
default: 1
1312+
title: Page
1313+
description: Page number
1314+
- name: pageSize
1315+
in: query
1316+
required: false
1317+
schema:
1318+
type: integer
1319+
maximum: 100
1320+
minimum: 1
1321+
description: Items per page
1322+
default: 10
1323+
title: Pagesize
1324+
description: Items per page
1325+
responses:
1326+
'200':
1327+
description: Successful Response
1328+
content:
1329+
application/json:
1330+
schema:
1331+
$ref: '#/components/schemas/PaginatedResponse_UserActivityResponse_'
1332+
'422':
1333+
description: Validation Error
1334+
content:
1335+
application/json:
1336+
schema:
1337+
$ref: '#/components/schemas/HTTPValidationError'
12741338
/:
12751339
get:
12761340
summary: Health check
@@ -1579,6 +1643,20 @@ components:
15791643
- data
15801644
- pagination
15811645
title: PaginatedResponse[FlagResponse]
1646+
PaginatedResponse_UserActivityResponse_:
1647+
properties:
1648+
data:
1649+
items:
1650+
$ref: '#/components/schemas/UserActivityResponse'
1651+
type: array
1652+
title: Data
1653+
pagination:
1654+
$ref: '#/components/schemas/Pagination'
1655+
type: object
1656+
required:
1657+
- data
1658+
- pagination
1659+
title: PaginatedResponse[UserActivityResponse]
15821660
PaginatedResponse_VideoSummary_:
15831661
properties:
15841662
data:
@@ -1743,6 +1821,31 @@ components:
17431821
- email
17441822
- userId
17451823
title: User
1824+
UserActivityResponse:
1825+
properties:
1826+
userId:
1827+
type: string
1828+
format: uuid
1829+
title: Userid
1830+
activityType:
1831+
type: string
1832+
title: Activitytype
1833+
activityId:
1834+
type: string
1835+
format: uuid
1836+
title: Activityid
1837+
activityTimestamp:
1838+
type: string
1839+
format: date-time
1840+
title: Activitytimestamp
1841+
type: object
1842+
required:
1843+
- userId
1844+
- activityType
1845+
- activityId
1846+
- activityTimestamp
1847+
title: UserActivityResponse
1848+
description: API response representation for a single user activity item.
17461849
UserCreateRequest:
17471850
properties:
17481851
firstName:
@@ -1873,7 +1976,7 @@ components:
18731976
description:
18741977
anyOf:
18751978
- type: string
1876-
maxLength: 1000
1979+
maxLength: 2000
18771980
- type: 'null'
18781981
title: Description
18791982
tags:
@@ -2127,7 +2230,7 @@ components:
21272230
description:
21282231
anyOf:
21292232
- type: string
2130-
maxLength: 1000
2233+
maxLength: 2000
21312234
- type: 'null'
21322235
title: Description
21332236
tags:

0 commit comments

Comments
 (0)