Skip to content

Commit bda5367

Browse files
authored
Update logs endpoint (#1750)
* Update logs endpoint behavior * update tests * change gitignore to upload logs * disable logs tests * address comment * disable logs tests 2 * disable logs tests 3 * disable logs tests 4 * disable logs tests 5 * disable logs tests 6 * Revert "disable logs tests" This reverts commit bf235c7.
1 parent a804f3d commit bda5367

File tree

6 files changed

+94
-92
lines changed

6 files changed

+94
-92
lines changed

gateway/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,5 @@ GitHub.sublime-settings
137137
!.vscode/extensions.json
138138
.history
139139

140-
tests/resources/fake_media/*
140+
tests/resources/fake_media/**/arguments/*
141+
!tests/resources/fake_media/**/logs/*.log

gateway/api/access_policies/jobs.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,28 @@ def can_read_result(user: type[AbstractUser], job: Job) -> bool:
6868
)
6969
return has_access
7070

71+
@staticmethod
72+
def can_read_logs(user: type[AbstractUser], job: Job) -> bool:
73+
"""
74+
Checks if the user has permissions to read the result of a job:
75+
76+
Args:
77+
user: Django user from the request
78+
job: Job instance against to check the permission
79+
80+
Returns:
81+
bool: True or False in case the user has permissions
82+
"""
83+
84+
has_access = user.id == job.author.id
85+
if not has_access:
86+
logger.warning(
87+
"User [%s] has no access to read the result of the job [%s].",
88+
user.username,
89+
job.author,
90+
)
91+
return has_access
92+
7193
@staticmethod
7294
def can_save_result(user: type[AbstractUser], job: Job) -> bool:
7395
"""

gateway/api/use_cases/jobs/get_logs.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
from django.contrib.auth.models import AbstractUser
88

9+
from api.access_policies.jobs import JobAccessPolicies
910
from api.domain.exceptions.not_found_error import NotFoundError
1011
from api.domain.exceptions.forbidden_error import ForbiddenError
1112
from api.repositories.jobs import JobsRepository
1213
from api.access_policies.providers import ProviderAccessPolicy
14+
from api.services.storage.enums.working_dir import WorkingDir
15+
from api.services.storage.logs_storage import LogsStorage
1316

1417

1518
NO_LOGS_MSG: Final[str] = "No available logs"
@@ -37,14 +40,22 @@ def execute(self, job_id: UUID, user: AbstractUser) -> str:
3740
if job is None:
3841
raise NotFoundError(f"Job [{job_id}] not found")
3942

40-
# Case 1: Provider function - check provider access policy
41-
if job.program and job.program.provider:
42-
if ProviderAccessPolicy.can_access(user, job.program.provider):
43-
return job.logs
43+
if not JobAccessPolicies.can_read_logs(user, job):
44+
raise ForbiddenError(f"You don't have access to job [{job_id}]")
4445

45-
# Case 2: User is the author of the job
46-
elif user == job.author:
47-
return job.logs
46+
logs_storage = LogsStorage(
47+
username=user.username,
48+
working_dir=WorkingDir.USER_STORAGE,
49+
function_title=job.program.title,
50+
provider_name=job.program.provider.name if job.program.provider else None,
51+
)
4852

49-
# Access denied for all other cases
50-
raise ForbiddenError(f"You don't have access to job [{job_id}]")
53+
logs = logs_storage.get(job_id)
54+
55+
if logs is None:
56+
raise NotFoundError(f"Logs for job[{job_id}] are not found")
57+
58+
if len(logs) == 0:
59+
return "No logs available"
60+
61+
return logs

gateway/tests/api/test_job.py

Lines changed: 48 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ def _authorize(self, username="test_user"):
2222
user = models.User.objects.get(username=username)
2323
self.client.force_authenticate(user=user)
2424

25+
def _fake_media_root(self):
26+
media_root = os.path.join(
27+
os.path.dirname(os.path.abspath(__file__)),
28+
"..",
29+
"resources",
30+
"fake_media",
31+
)
32+
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
33+
return media_root
34+
2535
def test_job_non_auth_user(self):
2636
"""Tests job list non-authorized."""
2737
url = reverse("v1:jobs-list")
@@ -256,15 +266,7 @@ def test_job_provider_list_pagination(self):
256266

257267
def test_job_detail(self):
258268
"""Tests job detail authorized."""
259-
media_root = os.path.join(
260-
os.path.dirname(os.path.abspath(__file__)),
261-
"..",
262-
"resources",
263-
"fake_media",
264-
)
265-
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
266-
267-
with self.settings(MEDIA_ROOT=media_root):
269+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
268270
self._authorize()
269271

270272
jobs_response = self.client.get(
@@ -276,15 +278,7 @@ def test_job_detail(self):
276278

277279
def test_job_detail_without_result_param(self):
278280
"""Tests job detail authorized."""
279-
media_root = os.path.join(
280-
os.path.dirname(os.path.abspath(__file__)),
281-
"..",
282-
"resources",
283-
"fake_media",
284-
)
285-
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
286-
287-
with self.settings(MEDIA_ROOT=media_root):
281+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
288282
self._authorize()
289283

290284
jobs_response = self.client.get(
@@ -297,15 +291,7 @@ def test_job_detail_without_result_param(self):
297291

298292
def test_job_detail_without_result_file(self):
299293
"""Tests job detail authorized."""
300-
media_root = os.path.join(
301-
os.path.dirname(os.path.abspath(__file__)),
302-
"..",
303-
"resources",
304-
"fake_media",
305-
)
306-
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
307-
308-
with self.settings(MEDIA_ROOT=media_root):
294+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
309295
self._authorize()
310296

311297
jobs_response = self.client.get(
@@ -340,15 +326,7 @@ def test_not_authorized_job_detail(self):
340326

341327
def test_job_save_result(self):
342328
"""Tests job results save."""
343-
media_root = os.path.join(
344-
os.path.dirname(os.path.abspath(__file__)),
345-
"..",
346-
"resources",
347-
"fake_media",
348-
)
349-
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
350-
351-
with self.settings(MEDIA_ROOT=media_root):
329+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
352330
self._authorize()
353331
job_id = "57fc2e4d-267f-40c6-91a3-38153272e764"
354332
jobs_response = self.client.post(
@@ -465,15 +443,7 @@ def test_user_has_access_to_job_result_from_provider_function(self):
465443
User has access to job result from a function provider
466444
as the authot of the job
467445
"""
468-
media_root = os.path.join(
469-
os.path.dirname(os.path.abspath(__file__)),
470-
"..",
471-
"resources",
472-
"fake_media",
473-
)
474-
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
475-
476-
with self.settings(MEDIA_ROOT=media_root):
446+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
477447
self._authorize()
478448

479449
jobs_response = self.client.get(
@@ -488,15 +458,7 @@ def test_provider_admin_has_no_access_to_job_result_from_provider_function(self)
488458
A provider admin has no access to job result from a function provider
489459
if it's not the author of the job
490460
"""
491-
media_root = os.path.join(
492-
os.path.dirname(os.path.abspath(__file__)),
493-
"..",
494-
"resources",
495-
"fake_media",
496-
)
497-
media_root = os.path.normpath(os.path.join(os.getcwd(), media_root))
498-
499-
with self.settings(MEDIA_ROOT=media_root):
461+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
500462
user = models.User.objects.get(username="test_user_3")
501463
self.client.force_authenticate(user=user)
502464

@@ -529,47 +491,51 @@ def test_stop_job(self):
529491

530492
def test_job_logs_by_author_for_function_without_provider(self):
531493
"""Tests job log by job author."""
532-
self._authorize()
494+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
495+
self._authorize()
533496

534-
jobs_response = self.client.get(
535-
reverse("v1:jobs-logs", args=["57fc2e4d-267f-40c6-91a3-38153272e764"]),
536-
format="json",
537-
)
538-
self.assertEqual(jobs_response.status_code, status.HTTP_200_OK)
539-
self.assertEqual(jobs_response.data.get("logs"), "log entry 2")
497+
jobs_response = self.client.get(
498+
reverse("v1:jobs-logs", args=["57fc2e4d-267f-40c6-91a3-38153272e764"]),
499+
format="json",
500+
)
501+
self.assertEqual(jobs_response.status_code, status.HTTP_200_OK)
502+
self.assertEqual(jobs_response.data.get("logs"), "log entry 2")
540503

541504
def test_job_logs_by_author_for_function_with_provider(self):
542505
"""Tests job log by job author."""
543-
self._authorize()
506+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
507+
self._authorize()
544508

545-
jobs_response = self.client.get(
546-
reverse("v1:jobs-logs", args=["1a7947f9-6ae8-4e3d-ac1e-e7d608deec85"]),
547-
format="json",
548-
)
549-
self.assertEqual(jobs_response.status_code, status.HTTP_403_FORBIDDEN)
509+
jobs_response = self.client.get(
510+
reverse("v1:jobs-logs", args=["1a7947f9-6ae8-4e3d-ac1e-e7d608deec85"]),
511+
format="json",
512+
)
513+
self.assertEqual(jobs_response.status_code, status.HTTP_403_FORBIDDEN)
550514

551515
def test_job_logs_by_function_provider(self):
552516
"""Tests job log by fuction provider."""
553-
user = models.User.objects.get(username="test_user_2")
554-
self.client.force_authenticate(user=user)
517+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
518+
user = models.User.objects.get(username="test_user_2")
519+
self.client.force_authenticate(user=user)
555520

556-
jobs_response = self.client.get(
557-
reverse("v1:jobs-logs", args=["1a7947f9-6ae8-4e3d-ac1e-e7d608deec85"]),
558-
format="json",
559-
)
560-
self.assertEqual(jobs_response.status_code, status.HTTP_200_OK)
561-
self.assertEqual(jobs_response.data.get("logs"), "log entry 1")
521+
jobs_response = self.client.get(
522+
reverse("v1:jobs-logs", args=["1a7947f9-6ae8-4e3d-ac1e-e7d608deec85"]),
523+
format="json",
524+
)
525+
self.assertEqual(jobs_response.status_code, status.HTTP_200_OK)
526+
self.assertEqual(jobs_response.data.get("logs"), "log entry 1")
562527

563528
def test_job_logs(self):
564529
"""Tests job log non-authorized."""
565-
user = models.User.objects.get(username="test_user_3")
566-
self.client.force_authenticate(user=user)
530+
with self.settings(MEDIA_ROOT=self._fake_media_root()):
531+
user = models.User.objects.get(username="test_user_3")
532+
self.client.force_authenticate(user=user)
567533

568-
jobs_response = self.client.get(
569-
reverse("v1:jobs-logs", args=["1a7947f9-6ae8-4e3d-ac1e-e7d608deec85"]),
570-
format="json",
571-
)
572-
self.assertEqual(jobs_response.status_code, status.HTTP_403_FORBIDDEN)
534+
jobs_response = self.client.get(
535+
reverse("v1:jobs-logs", args=["1a7947f9-6ae8-4e3d-ac1e-e7d608deec85"]),
536+
format="json",
537+
)
538+
self.assertEqual(jobs_response.status_code, status.HTTP_403_FORBIDDEN)
573539

574540
def test_runtime_jobs_post(self):
575541
"""Tests runtime jobs POST endpoint."""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
log entry 2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
log entry 1

0 commit comments

Comments
 (0)