Skip to content

Commit

Permalink
feat: improve job history view
Browse files Browse the repository at this point in the history
  • Loading branch information
mytlogos committed Aug 6, 2022
1 parent 7373534 commit ff48578
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 12 deletions.
46 changes: 45 additions & 1 deletion packages/core/src/database/contexts/jobContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
Id,
JobStatSummary,
JobTrack,
QueryJobHistory,
Paginated,
} from "../../types";
import { isString, promiseMultiSingle, multiSingle } from "../../tools";
import logger from "../../logger";
Expand Down Expand Up @@ -448,7 +450,6 @@ export class JobContext extends SubContext {
public async getJobHistoryStream(since: Date, limit: number): Promise<TypedQuery<JobHistoryItem>> {
let query = "SELECT * FROM job_history WHERE start < ? ORDER BY start DESC";
const values = [since.toISOString()] as any[];
console.log(values);

if (limit >= 0) {
query += " LIMIT ?;";
Expand All @@ -461,6 +462,49 @@ export class JobContext extends SubContext {
return this.query("SELECT * FROM job_history ORDER BY start;");
}

/**
* Return a paginated query result.
* Returns at most 1000 items but at least 5.
*
* @param filter the query filter
* @returns an array of items
*/
public async getJobHistoryPaginated(filter: QueryJobHistory): Promise<Paginated<JobHistoryItem, "start">> {
let conditions = "WHERE start < ?";
const values: any[] = [filter.since.toISOString()];

if (filter.name) {
values.push(`%${filter.name}%`);
conditions += " AND name like ?";
}

if (filter.type) {
values.push(filter.type);
conditions += " AND type = ?";
}

if (filter.result) {
values.push(filter.result);
conditions += " AND result = ?";
}

conditions += " ORDER BY start DESC";
const countValues = [...values];

const limit = " LIMIT ?;";
values.push(Math.max(Math.min(filter.limit, 1000), 5));

const totalPromise = this.query("SELECT count(*) as total FROM job_history " + conditions, countValues);
const items: JobHistoryItem[] = await this.query("SELECT * FROM job_history " + conditions + limit, values);
const [{ total }]: [{ total: number }] = await totalPromise;

return {
items,
next: items[items.length - 1] && new Date(items[items.length - 1].start),
total,
};
}

private async addJobHistory(jobs: JobItem | JobItem[], finished: Date): EmptyPromise {
await promiseMultiSingle(jobs, async (value: JobItem) => {
let args = value.arguments;
Expand Down
18 changes: 17 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,8 @@ export interface JobStats extends AllJobStats {
name: string;
}

export type JobHistoryResult = "warning" | "failed" | "success";

/**
* @openapi
* components:
Expand Down Expand Up @@ -1475,7 +1477,7 @@ export type JobHistoryItem = Pick<JobItem, "id" | "type" | "name" | "deleteAfter
scheduled_at: Date;
start: Date;
end: Date;
result: string;
result: JobHistoryResult;
message: string;
context: string;
created?: number;
Expand All @@ -1489,6 +1491,20 @@ export type JobHistoryItem = Pick<JobItem, "id" | "type" | "name" | "deleteAfter
duration?: number;
};

export interface Paginated<T, K extends keyof T> {
items: T[];
next: T[K];
total: number;
}

export interface QueryJobHistory {
since: Date;
limit: number;
type?: ScrapeName;
result?: JobHistoryResult;
name?: string;
}

export interface Modification {
created: number;
deleted: number;
Expand Down
23 changes: 23 additions & 0 deletions packages/server/src/api/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { jobStorage } from "enterprise-core/dist/database/storages/storage";
import { Router } from "express";
import {
GetHistoryJobs,
GetHistoryJobsPaginated,
getHistoryJobsPaginatedSchema,
getHistoryJobsSchema,
GetJobDetails,
getJobDetailsSchema,
Expand Down Expand Up @@ -29,6 +31,26 @@ export const getHistoryJobs = createHandler(
{ query: getHistoryJobsSchema },
);

export const getHistoryJobsPaginated = createHandler(
(req) => {
const query = castQuery<GetHistoryJobsPaginated>(req);
let since = new Date(query.since || "");

if (Number.isNaN(since.getTime())) {
since = new Date();
}

return jobStorage.getJobHistoryPaginated({
since,
limit: query.limit ?? 0,
name: query.name,
result: query.result,
type: query.type,
});
},
{ query: getHistoryJobsPaginatedSchema },
);

export const postJobEnable = createHandler((req) => {
const { id, enabled }: PostJobEnable = req.body;
return jobStorage.updateJobsEnable(id, enabled);
Expand Down Expand Up @@ -128,6 +150,7 @@ export function jobsRouter(): Router {
* description: queried job history
*/
router.get("/history", getHistoryJobs);
router.get("/history-paginated", getHistoryJobsPaginated);

/**
* @openapi
Expand Down
38 changes: 38 additions & 0 deletions packages/server/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {
AppEventProgram,
AppEventType,
Id,
JobHistoryResult,
Json,
Link,
MediumInWait,
MediumInWaitSearch,
MinList,
QueryItems,
ScrapeName,
SimpleEpisode,
SimpleMedium,
TimeBucket,
Expand Down Expand Up @@ -838,6 +840,42 @@ export const getHistoryJobsSchema: JSONSchemaType<GetHistoryJobs> = {
},
};

export interface GetHistoryJobsPaginated {
since?: string;
name?: string;
type?: ScrapeName;
result?: JobHistoryResult;
limit?: number;
}

export const getHistoryJobsPaginatedSchema: JSONSchemaType<GetHistoryJobsPaginated> = {
$id: "/GetHistoryJobsPaginated",
type: "object",
properties: {
since: { ...string(), nullable: true },
name: { ...string(), nullable: true },
limit: { ...integer({ minimum: 1, maximum: 1000 }), nullable: true },
type: {
type: "string",
enum: [
ScrapeName.checkTocs,
ScrapeName.feed,
ScrapeName.news,
ScrapeName.newsAdapter,
ScrapeName.oneTimeToc,
ScrapeName.oneTimeUser,
ScrapeName.queueExternalUser,
ScrapeName.queueTocs,
ScrapeName.remapMediaParts,
ScrapeName.searchForToc,
ScrapeName.toc,
],
nullable: true,
},
result: { type: "string", enum: ["failed", "success", "warning"], nullable: true },
},
};

export interface PostJobEnable {
id: Id;
enabled: boolean;
Expand Down
10 changes: 9 additions & 1 deletion packages/website/src/Httpclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
} from "./siteTypes";
import { AddPart, AppEvent, AppEventFilter, EmptyPromise, JobStatSummary } from "enterprise-core/src/types";
import { HookTest, HookTestV2, Status } from "enterprise-server/src/types";
import { CustomHook, Id, Notification, Nullable, SimpleUser } from "enterprise-core/dist/types";
import { GetHistoryJobsPaginated } from "enterprise-server/dist/validation";
import { CustomHook, Id, Notification, Nullable, Paginated, SimpleUser } from "enterprise-core/dist/types";
import qs from "qs";

/**
Expand Down Expand Up @@ -73,6 +74,9 @@ const restApi = createRestDefinition({
history: {
get: true,
},
"history-paginated": {
get: true,
},
stats: {
summary: {
get: true,
Expand Down Expand Up @@ -479,6 +483,10 @@ export const HttpClient = {
return this.queryServer(serverRestApi.api.user.jobs.history.get, { since, limit });
},

getJobHistoryPaginated(query: GetHistoryJobsPaginated): Promise<Paginated<JobHistoryItem, "start">> {
return this.queryServer(serverRestApi.api.user.jobs["history-paginated"].get, query);
},

postJobEnabled(id: number, enabled: boolean): Promise<Job[]> {
return this.queryServer(serverRestApi.api.user.jobs.enable.post, { id, enabled });
},
Expand Down
Loading

0 comments on commit ff48578

Please sign in to comment.