Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 8cb761a

Browse files
authored
Implement with_tasks option to expand the task information (#3343)
* Implement with_tasks option to expand the task information * Added tests * format
1 parent fc4e698 commit 8cb761a

File tree

7 files changed

+88
-22
lines changed

7 files changed

+88
-22
lines changed

src/ApiService/ApiService/Functions/Jobs.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,14 @@ private async Task<HttpResponseData> Get(HttpRequestData req) {
135135

136136
static JobTaskInfo TaskToJobTaskInfo(Task t) => new(t.TaskId, t.Config.Task.Type, t.State);
137137

138-
// TODO: search.WithTasks is not checked in Python code?
139-
140-
var taskInfo = await _context.TaskOperations.SearchStates(jobId).Select(TaskToJobTaskInfo).ToListAsync();
141-
return await RequestHandling.Ok(req, JobResponse.ForJob(job, taskInfo));
138+
var tasks = _context.TaskOperations.SearchStates(jobId);
139+
if (search.WithTasks ?? false) {
140+
var ts = await tasks.ToListAsync();
141+
return await RequestHandling.Ok(req, JobResponse.ForJob(job, ts));
142+
} else {
143+
var taskInfo = await tasks.Select(TaskToJobTaskInfo).ToListAsync();
144+
return await RequestHandling.Ok(req, JobResponse.ForJob(job, taskInfo));
145+
}
142146
}
143147

144148
var jobs = await _context.JobOperations.SearchState(states: search.State ?? Enumerable.Empty<JobState>()).ToListAsync();

src/ApiService/ApiService/OneFuzzTypes/Model.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,8 @@ public record Task(
282282
ISecret<Authentication>? Auth = null,
283283
DateTimeOffset? Heartbeat = null,
284284
DateTimeOffset? EndTime = null,
285-
StoredUserInfo? UserInfo = null) : StatefulEntityBase<TaskState>(State) {
285+
StoredUserInfo? UserInfo = null) : StatefulEntityBase<TaskState>(State), IJobTaskInfo {
286+
public TaskType Type => Config.Task.Type;
286287
}
287288

288289
public record TaskEvent(
@@ -898,11 +899,19 @@ public JobConfig Truncate(int maxLength) {
898899
}
899900
}
900901

902+
[JsonDerivedType(typeof(Task), typeDiscriminator: "Task")]
903+
[JsonDerivedType(typeof(JobTaskInfo), typeDiscriminator: "JobTaskInfo")]
904+
public interface IJobTaskInfo {
905+
Guid TaskId { get; }
906+
TaskType Type { get; }
907+
TaskState State { get; }
908+
}
909+
901910
public record JobTaskInfo(
902911
Guid TaskId,
903912
TaskType Type,
904913
TaskState State
905-
);
914+
) : IJobTaskInfo;
906915

907916
public record Job(
908917
[PartitionKey][RowKey] Guid JobId,

src/ApiService/ApiService/OneFuzzTypes/Responses.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,13 @@ public record JobResponse(
9696
JobConfig Config,
9797
string? Error,
9898
DateTimeOffset? EndTime,
99-
List<JobTaskInfo>? TaskInfo,
99+
IEnumerable<IJobTaskInfo>? TaskInfo,
100100
StoredUserInfo? UserInfo,
101101
[property: JsonPropertyName("Timestamp")] // must retain capital T for backcompat
102102
DateTimeOffset? Timestamp
103103
// not including UserInfo from Job model
104104
) : BaseResponse() {
105-
public static JobResponse ForJob(Job j, List<JobTaskInfo>? taskInfo)
105+
public static JobResponse ForJob(Job j, IEnumerable<IJobTaskInfo>? taskInfo)
106106
=> new(
107107
JobId: j.JobId,
108108
State: j.State,

src/ApiService/IntegrationTests/JobsTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,55 @@ public async Async.Task Post_CreatesJob_AndContainer() {
175175
var metadata = Assert.Single(container.Value);
176176
Assert.Equal(new KeyValuePair<string, string>("container_type", "logs"), metadata);
177177
}
178+
179+
180+
[Fact]
181+
public async Async.Task Get_CanFindSpecificJobWithTaskInfo() {
182+
183+
var taskConfig = new TaskConfig(_jobId, new List<Guid>(), new TaskDetails(TaskType.Coverage, 60));
184+
var task = new Task(_jobId, Guid.NewGuid(), TaskState.Running, Os.Windows, taskConfig);
185+
186+
await Context.InsertAll(
187+
new Job(_jobId, JobState.Stopped, _config, null), task);
188+
189+
var func = new Jobs(Context, LoggerProvider.CreateLogger<Jobs>());
190+
191+
var ctx = new TestFunctionContext();
192+
var result = await func.Run(TestHttpRequestData.FromJson("GET", new JobSearch(JobId: _jobId, WithTasks: false)), ctx);
193+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
194+
195+
var response = BodyAs<JobResponse>(result);
196+
Assert.Equal(_jobId, response.JobId);
197+
Assert.NotNull(response.TaskInfo);
198+
var returnedTasks = response.TaskInfo.OfType<JobTaskInfo>().ToList();
199+
Assert.NotEmpty(returnedTasks);
200+
Assert.Equal(task.TaskId, returnedTasks[0].TaskId);
201+
Assert.Equal(task.State, returnedTasks[0].State);
202+
Assert.Equal(task.Config.Task.Type, returnedTasks[0].Type);
203+
}
204+
205+
[Fact]
206+
public async Async.Task Get_CanFindSpecificJobWithFullTask() {
207+
var taskConfig = new TaskConfig(_jobId, new List<Guid>(), new TaskDetails(TaskType.Coverage, 60));
208+
var task = new Task(_jobId, Guid.NewGuid(), TaskState.Running, Os.Windows, taskConfig);
209+
210+
await Context.InsertAll(
211+
new Job(_jobId, JobState.Stopped, _config, null), task);
212+
213+
var func = new Jobs(Context, LoggerProvider.CreateLogger<Jobs>());
214+
215+
var ctx = new TestFunctionContext();
216+
var result = await func.Run(TestHttpRequestData.FromJson("GET", new JobSearch(JobId: _jobId, WithTasks: true)), ctx);
217+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
218+
219+
var response = BodyAs<JobResponse>(result);
220+
Assert.Equal(_jobId, response.JobId);
221+
Assert.NotNull(response.TaskInfo);
222+
var returnedTasks = response.TaskInfo.OfType<Task>().ToList();
223+
Assert.NotEmpty(returnedTasks);
224+
Assert.Equal(task.TaskId, returnedTasks[0].TaskId);
225+
Assert.Equal(task.State, returnedTasks[0].State);
226+
Assert.Equal(task.Config.Task.Type, returnedTasks[0].Type);
227+
228+
}
178229
}

src/cli/onefuzz/api.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,14 +1308,16 @@ def delete(self, job_id: UUID_EXPANSION) -> models.Job:
13081308
"DELETE", models.Job, data=requests.JobGet(job_id=job_id_expanded)
13091309
)
13101310

1311-
def get(self, job_id: UUID_EXPANSION) -> models.Job:
1311+
def get(self, job_id: UUID_EXPANSION, with_tasks: bool = False) -> models.Job:
13121312
"""Get information about a specific job"""
13131313
job_id_expanded = self._disambiguate_uuid(
13141314
"job_id", job_id, lambda: [str(x.job_id) for x in self.list()]
13151315
)
13161316
self.logger.debug("get job: %s", job_id_expanded)
13171317
job = self._req_model(
1318-
"GET", models.Job, data=requests.JobGet(job_id=job_id_expanded)
1318+
"GET",
1319+
models.Job,
1320+
data=requests.JobGet(job_id=job_id_expanded, with_tasks=with_tasks),
13191321
)
13201322
return job
13211323

src/pytypes/onefuzztypes/models.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -463,17 +463,6 @@ class JobTaskInfo(BaseModel):
463463
state: TaskState
464464

465465

466-
class Job(BaseModel):
467-
timestamp: Optional[datetime] = Field(alias="Timestamp")
468-
job_id: UUID = Field(default_factory=uuid4)
469-
state: JobState = Field(default=JobState.init)
470-
config: JobConfig
471-
error: Optional[str]
472-
end_time: Optional[datetime] = None
473-
task_info: Optional[List[JobTaskInfo]]
474-
user_info: Optional[UserInfo]
475-
476-
477466
class TaskHeartbeatEntry(BaseModel):
478467
task_id: UUID
479468
job_id: Optional[UUID]
@@ -757,6 +746,17 @@ class Task(BaseModel):
757746
user_info: Optional[UserInfo]
758747

759748

749+
class Job(BaseModel):
750+
timestamp: Optional[datetime] = Field(alias="Timestamp")
751+
job_id: UUID = Field(default_factory=uuid4)
752+
state: JobState = Field(default=JobState.init)
753+
config: JobConfig
754+
error: Optional[str]
755+
end_time: Optional[datetime] = None
756+
task_info: Optional[List[Union[Task, JobTaskInfo]]]
757+
user_info: Optional[UserInfo]
758+
759+
760760
class NetworkConfig(BaseModel):
761761
address_space: str = Field(default="10.0.0.0/8")
762762
subnet: str = Field(default="10.0.0.0/16")

src/pytypes/onefuzztypes/requests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ class BaseRequest(BaseModel):
3939

4040
class JobGet(BaseRequest):
4141
job_id: UUID
42+
with_tasks: Optional[bool]
4243

4344

4445
class JobSearch(BaseRequest):
4546
job_id: Optional[UUID]
4647
state: Optional[List[JobState]]
4748
task_state: Optional[List[TaskState]]
48-
with_tasks: Optional[bool]
4949

5050

5151
class NotificationCreate(BaseRequest, NotificationConfig):

0 commit comments

Comments
 (0)