Skip to content

Commit b24c005

Browse files
author
kobo-bot[bot]
committed
Merge branch 'release/2.025.43' into release/2.025.47
2 parents bb4b9f1 + 1182550 commit b24c005

File tree

12 files changed

+283
-55
lines changed

12 files changed

+283
-55
lines changed

jsapp/js/api/models/paginatedAuditLogResponseList.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ The endpoints are grouped by area of intended use. Each category contains relate
1212
import type { AuditLogResponse } from './auditLogResponse'
1313

1414
export interface PaginatedAuditLogResponseList {
15-
count: number
1615
/** @nullable */
1716
next?: string | null
1817
/** @nullable */

jsapp/js/api/models/paginatedProjectHistoryLogResponseList.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ The endpoints are grouped by area of intended use. Each category contains relate
1212
import type { ProjectHistoryLogResponse } from './projectHistoryLogResponse'
1313

1414
export interface PaginatedProjectHistoryLogResponseList {
15-
count: number
1615
/** @nullable */
1716
next?: string | null
1817
/** @nullable */
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Generated by orval v7.10.0 🍺
3+
* Do not edit manually.
4+
* KoboToolbox Primary API
5+
* This page documents all KoboToolbox API endpoints, except for those implementing the OpenRosa protocol, which are [documented separately](/api/openrosa/docs/).
6+
7+
The endpoints are grouped by area of intended use. Each category contains related endpoints, with detailed documentation on usage and configuration. Use this as a reference to quickly find the right endpoint for managing projects, forms, data, permissions, integrations, logs, and organizational resources.
8+
9+
**General note**: All projects (whether deployed or draft), as well as all library content (questions, blocks, templates, and collections) in the user-facing application are represented in the API as "assets".
10+
* OpenAPI spec version: 2.0.0 (api_v2)
11+
*/
12+
import type { SuperUserAccessLogResponse } from './superUserAccessLogResponse'
13+
14+
export interface PaginatedSuperUserAccessLogResponseList {
15+
/** @nullable */
16+
next?: string | null
17+
/** @nullable */
18+
previous?: string | null
19+
results: SuperUserAccessLogResponse[]
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Generated by orval v7.10.0 🍺
3+
* Do not edit manually.
4+
* KoboToolbox Primary API
5+
* This page documents all KoboToolbox API endpoints, except for those implementing the OpenRosa protocol, which are [documented separately](/api/openrosa/docs/).
6+
7+
The endpoints are grouped by area of intended use. Each category contains related endpoints, with detailed documentation on usage and configuration. Use this as a reference to quickly find the right endpoint for managing projects, forms, data, permissions, integrations, logs, and organizational resources.
8+
9+
**General note**: All projects (whether deployed or draft), as well as all library content (questions, blocks, templates, and collections) in the user-facing application are represented in the API as "assets".
10+
* OpenAPI spec version: 2.0.0 (api_v2)
11+
*/
12+
import type { SuperUserAccessLogResponseMetadata } from './superUserAccessLogResponseMetadata'
13+
14+
export interface SuperUserAccessLogResponse {
15+
user: string
16+
date_created: string
17+
username: string
18+
metadata: SuperUserAccessLogResponseMetadata
19+
user_uid: string
20+
count: number
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Generated by orval v7.10.0 🍺
3+
* Do not edit manually.
4+
* KoboToolbox Primary API
5+
* This page documents all KoboToolbox API endpoints, except for those implementing the OpenRosa protocol, which are [documented separately](/api/openrosa/docs/).
6+
7+
The endpoints are grouped by area of intended use. Each category contains related endpoints, with detailed documentation on usage and configuration. Use this as a reference to quickly find the right endpoint for managing projects, forms, data, permissions, integrations, logs, and organizational resources.
8+
9+
**General note**: All projects (whether deployed or draft), as well as all library content (questions, blocks, templates, and collections) in the user-facing application are represented in the API as "assets".
10+
* OpenAPI spec version: 2.0.0 (api_v2)
11+
*/
12+
13+
export type SuperUserAccessLogResponseMetadata = {
14+
source?: string
15+
auth_type?: string
16+
ip_address?: string
17+
initial_user_uid?: string
18+
initial_user_username?: string
19+
authorized_app_name?: string
20+
}

jsapp/js/api/react-query/server-logs-superusers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ import type { ExportHistoryResponse } from '../models/exportHistoryResponse'
3333

3434
import type { ExportListResponse } from '../models/exportListResponse'
3535

36-
import type { PaginatedAccessLogResponseList } from '../models/paginatedAccessLogResponseList'
37-
3836
import type { PaginatedAuditLogResponseList } from '../models/paginatedAuditLogResponseList'
3937

4038
import type { PaginatedProjectHistoryLogResponseList } from '../models/paginatedProjectHistoryLogResponseList'
4139

40+
import type { PaginatedSuperUserAccessLogResponseList } from '../models/paginatedSuperUserAccessLogResponseList'
41+
4242
import type { PaginatedUserReportsListResponseList } from '../models/paginatedUserReportsListResponseList'
4343

4444
import type { ProjectHistoryLogsListParams } from '../models/projectHistoryLogsListParams'
@@ -78,7 +78,7 @@ Submissions will be grouped together by user by hour
7878
7979
*/
8080
export type accessLogsListResponse200 = {
81-
data: PaginatedAccessLogResponseList
81+
data: PaginatedSuperUserAccessLogResponseList
8282
status: 200
8383
}
8484

kobo/apps/audit_log/schema_extensions/v2/access_logs/serializers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
},
2929
)
3030

31+
SuperUserAccessLogResponse = inline_serializer_class(
32+
name='SuperUserAccessLogResponse',
33+
fields=AccessLogResponse().get_fields(),
34+
)
35+
3136

3237
ExportListResponse = inline_serializer_class(
3338
name='ExportListResponse',

kobo/apps/audit_log/tests/api/v2/test_api_audit_log.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def test_results_have_expected_fields(self):
7171
date_created=now,
7272
)
7373
response = self.client.get(self.url)
74-
self.assertEqual(response.data['count'], 1)
74+
self.assertEqual(len(response.data['results']), 1)
7575
ph_log = response.data['results'][0]
7676
self.assertListEqual(
7777
sorted(list(ph_log.keys())),
@@ -121,7 +121,7 @@ def test_results_are_sorted_by_date_descending(self):
121121
date_created=now,
122122
)
123123
response = self.client.get(self.url)
124-
self.assertEqual(response.data['count'], 2)
124+
self.assertEqual(len(response.data['results']), 2)
125125
self.assertEqual(
126126
response.data['results'][0]['date_created'],
127127
now.strftime('%Y-%m-%dT%H:%M:%SZ'),
@@ -164,7 +164,7 @@ def test_results_can_be_searched_by_subtype(self):
164164
f'{self.url}?q=metadata__log_subtype:'
165165
f'{PROJECT_HISTORY_LOG_PERMISSION_SUBTYPE}'
166166
)
167-
self.assertEqual(response.data['count'], 1)
167+
self.assertEqual(len(response.data['results']), 1)
168168
self.assertEqual(
169169
response.data['results'][0]['metadata']['log_subtype'],
170170
PROJECT_HISTORY_LOG_PERMISSION_SUBTYPE,
@@ -250,9 +250,7 @@ def test_list_as_superuser(self):
250250
},
251251
]
252252
response = self.client.get(self.url)
253-
audit_logs_count = AuditLog.objects.count()
254253
assert response.status_code == status.HTTP_200_OK
255-
assert response.data['count'] == audit_logs_count
256254
assert response.data['results'] == expected
257255

258256
def test_filter_list(self):
@@ -295,7 +293,6 @@ def test_filter_list(self):
295293
audit_logs_count = AuditLog.objects.count()
296294
assert response.status_code == status.HTTP_200_OK
297295
assert audit_logs_count == 2
298-
assert response.data['count'] == 1
299296
assert response.data['results'] == expected
300297

301298
def test_view_log_from_deleted_user(self):
@@ -314,7 +311,7 @@ def test_view_log_from_deleted_user(self):
314311
self.login_user(username='adminuser', password='pass')
315312
response = self.client.get(self.url)
316313
assert response.status_code == status.HTTP_200_OK
317-
assert response.data['count'] == 1
314+
assert len(response.data['results']) == 1
318315
assert response.data['results'][0]['username'] is None
319316
assert response.data['results'][0]['user'] is None
320317

@@ -337,7 +334,7 @@ def test_show_user_access_logs_correctly_filters_to_user(self):
337334
self.force_login_user(user1)
338335

339336
response = self.client.get(self.url)
340-
self.assertEqual(response.data['count'], 1)
337+
self.assertEqual(len(response.data['results']), 1)
341338
self.assertEqual(response.data['results'][0]['username'], 'someuser')
342339

343340
def test_endpoint_ignores_querystring(self):
@@ -349,7 +346,7 @@ def test_endpoint_ignores_querystring(self):
349346
self.force_login_user(user1)
350347
response = self.client.get(f'{self.url}?q=user__username:anotheruser')
351348
# check we still only got logs for the logged-in user
352-
self.assertEqual(response.data['count'], 1)
349+
self.assertEqual(len(response.data['results']), 1)
353350
self.assertEqual(response.data['results'][0]['username'], 'someuser')
354351

355352
def test_endpoint_orders_results_by_date_desc(self):
@@ -393,7 +390,7 @@ def test_endpoint_groups_submissions(self):
393390
)
394391
response = self.client.get(self.url)
395392
# should return 1 submission group with 2 submissions
396-
self.assertEqual(response.data['count'], 1)
393+
self.assertEqual(len(response.data['results']), 1)
397394
result = response.data['results'][0]
398395
self.assertEqual(
399396
result['metadata']['auth_type'],
@@ -430,7 +427,7 @@ def test_show_all_access_logs_includes_all_users(self):
430427
AccessLog.objects.create(user=user2)
431428
self.force_login_user(admin)
432429
response = self.client.get(self.url)
433-
self.assertEqual(response.data['count'], 2)
430+
self.assertEqual(len(response.data['results']), 2)
434431
self.assertEqual(response.data['results'][0]['username'], 'anotheruser')
435432
self.assertEqual(response.data['results'][1]['username'], 'someuser')
436433

@@ -472,7 +469,7 @@ def test_endpoint_groups_submissions(self):
472469
)
473470
response = self.client.get(self.url)
474471
# should return 2 submission group with 2 submissions each
475-
self.assertEqual(response.data['count'], 2)
472+
self.assertEqual(len(response.data['results']), 2)
476473

477474
# user2's submission group should be first
478475
group1 = response.data['results'][0]
@@ -503,7 +500,7 @@ def test_can_search_access_logs_by_username(self):
503500
# only return logs from user1
504501
for audit_log_dict in response.data['results']:
505502
self.assertEqual(audit_log_dict['username'], 'anotheruser')
506-
self.assertEqual(response.data['count'], 1)
503+
self.assertEqual(len(response.data['results']), 1)
507504

508505
def test_can_search_access_logs_by_username_including_submission_groups(
509506
self,
@@ -532,7 +529,7 @@ def test_can_search_access_logs_by_username_including_submission_groups(
532529
AccessLog.objects.create(user=user2)
533530

534531
response = self.client.get(f'{self.url}?q=user__username:someuser')
535-
self.assertEqual(response.data['count'], 1)
532+
self.assertEqual(len(response.data['results']), 1)
536533
result = response.data['results'][0]
537534
self.assertEqual(result['username'], 'someuser')
538535
self.assertEqual(
@@ -559,7 +556,7 @@ def test_can_search_access_logs_by_date(self):
559556
self.assertEqual(response.status_code, status.HTTP_200_OK)
560557

561558
# should only return the log from tomorrow
562-
self.assertEqual(response.data['count'], 1)
559+
self.assertEqual(len(response.data['results']), 1)
563560
result = response.data['results'][0]
564561
self.assertEqual(
565562
result['date_created'], tomorrow.strftime('%Y-%m-%dT%H:%M:%SZ')
@@ -593,7 +590,7 @@ def test_can_search_access_logs_by_date_including_submission_groups(self):
593590
self.assertEqual(response.status_code, status.HTTP_200_OK)
594591

595592
# should only return the submission group
596-
self.assertEqual(response.data['count'], 1)
593+
self.assertEqual(len(response.data['results']), 1)
597594
group = response.data['results'][0]
598595
self.assertEqual(
599596
group['date_created'],
@@ -656,7 +653,7 @@ def test_show_project_history_logs_filters_to_project(self):
656653
metadata={**self.default_metadata, 'asset_uid': asset2.uid},
657654
)
658655
response = self.client.get(self.url)
659-
self.assertEqual(response.data['count'], 1)
656+
self.assertEqual(len(response.data['results']), 1)
660657
self.assertEqual(
661658
response.data['results'][0]['metadata']['asset_uid'], self.asset.uid
662659
)
@@ -754,7 +751,7 @@ def test_show_all_project_history_logs(self):
754751
},
755752
)
756753
response = self.client.get(self.url)
757-
self.assertEqual(response.data['count'], 2)
754+
self.assertEqual(len(response.data['results']), 2)
758755
self.assertEqual(
759756
response.data['results'][0]['metadata']['asset_uid'], asset2.uid
760757
)

kobo/apps/audit_log/views.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
ImportExportStatusChoices,
1414
ProjectHistoryLogExportTask,
1515
)
16-
from kpi.paginators import FastPagination, Paginated
16+
from kpi.paginators import FastPagination, NoCountPagination, Paginated
1717
from kpi.permissions import IsAuthenticated
1818
from kpi.renderers import BasicHTMLRenderer
1919
from kpi.tasks import export_task_in_background
@@ -30,6 +30,7 @@
3030
AccessLogResponse,
3131
ExportCreateResponse,
3232
ExportListResponse,
33+
SuperUserAccessLogResponse,
3334
)
3435
from .schema_extensions.v2.audit_logs.serializers import (
3536
AuditLogResponse,
@@ -89,15 +90,15 @@ class AuditLogViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
8990
'model_name__icontains',
9091
'metadata__icontains',
9192
]
92-
pagination_class = FastPagination
93+
pagination_class = NoCountPagination
9394

9495

9596
@extend_schema_view(
9697
list=extend_schema(
9798
tags=['Server logs (superusers)'],
9899
description=read_md('audit_log', 'access_logs/list'),
99100
responses=open_api_200_ok_response(
100-
AccessLogResponse,
101+
SuperUserAccessLogResponse,
101102
require_auth=False,
102103
validate_payload=False,
103104
raise_not_found=False,
@@ -116,7 +117,6 @@ class AllAccessLogViewSet(AuditLogViewSet):
116117
"""
117118
queryset = AccessLog.objects.with_submissions_grouped().order_by('-date_created')
118119
serializer_class = AccessLogSerializer
119-
pagination_class = Paginated
120120

121121

122122
@extend_schema_view(
@@ -165,9 +165,9 @@ class AllProjectHistoryLogViewSet(AuditLogViewSet):
165165
ViewSet for managing the all projects history
166166
167167
Available actions:
168-
- list → GET /api/v2/asset/project-history-logs/
169-
- export → GET /api/v2/asset/project-history-logs/export/
170-
- export → POST /api/v2/asset/project-history-logs/export/
168+
- list → GET /api/v2/project-history-logs/
169+
- export → GET /api/v2/project-history-logs/export/
170+
- export → POST /api/v2/project-history-logs/export/
171171
172172
Documentation:
173173
- docs/api/v2/project-history-log/list.md
@@ -286,9 +286,9 @@ class ProjectHistoryLogViewSet(
286286
ViewSet for managing the current project's history
287287
288288
Available actions:
289-
- action → GET /api/v2/asset/{uid_asset}/history/action/
290-
- export → POST /api/v2/asset/{uid_asset}/history/
291-
- list → GET /api/v2/asset/{uid_asset}/history/
289+
- action → GET /api/v2/assets/{uid_asset}/history/action/
290+
- export → POST /api/v2/assets/{uid_asset}/history/
291+
- list → GET /api/v2/assets/{uid_asset}/history/
292292
293293
Documentation:
294294
- docs/api/v2/history/action.md
@@ -300,6 +300,7 @@ class ProjectHistoryLogViewSet(
300300
model = ProjectHistoryLog
301301
permission_classes = (ViewProjectHistoryLogsPermission,)
302302
lookup_field = 'uid'
303+
pagination_class = FastPagination
303304

304305
def get_queryset(self):
305306
return self.model.objects.filter(metadata__asset_uid=self.asset_uid).order_by(

0 commit comments

Comments
 (0)