diff --git a/public/JobHandler/index.tsx b/public/JobHandler/index.tsx new file mode 100644 index 000000000..2035a8342 --- /dev/null +++ b/public/JobHandler/index.tsx @@ -0,0 +1,274 @@ +import React, { ReactChild } from "react"; +import { EuiLink } from "@elastic/eui"; +import { CoreSetup } from "../../../../src/core/public"; +import { jobSchedulerInstance } from "../context/JobSchedulerContext"; +import { CommonService, IndexService } from "../services"; +import { ReindexJobMetaData, RecoveryJobMetaData } from "../models/interfaces"; +import { ROUTES } from "../utils/constants"; + +const DetailLink = (props: { index: string }) => { + return {props.index}; +}; + +type TaskResult = { + found: boolean; + _source: { + completed: boolean; + response: { + failures: { + cause?: { + reason: string; + }; + }[]; + }; + error?: { + type: string; + reason: string; + }; + }; +}; + +export const EVENT_MAP = { + REINDEX_COMPLETE: "REINDEX_COMPLETE", + SPLIT_COMPLETE: "SPLIT_COMPLETE", + SHRINK_COMPLETE: "SHRINK_COMPLETE", +}; + +const triggerEvent = (eventName: string, data?: unknown) => { + const event = new CustomEvent(eventName, { + detail: data, + }); + window.dispatchEvent(event); +}; + +export const listenEvent = (eventName: string, callback: () => void) => { + window.addEventListener(eventName, callback); +}; + +export const destroyListener = (eventName: string, callback: () => void) => { + window.removeEventListener(eventName, callback); +}; + +export function JobHandlerRegister(core: CoreSetup) { + const indexService = new IndexService(core.http); + const commonService = new CommonService(core.http); + jobSchedulerInstance.addCallback({ + callbackName: "callbackForReindex", + callback: async (job: ReindexJobMetaData) => { + const extras = job.extras; + const tasksResult = await commonService.apiCaller({ + endpoint: "transport.request", + data: { + path: `.tasks/_doc/${extras.taskId}`, + method: "GET", + }, + }); + if (tasksResult.ok) { + const { _source, found } = tasksResult.response; + const { completed, response, error } = (_source || {}) as TaskResult["_source"]; + const { failures } = response; + if (completed && found) { + if (!failures.length && !error?.reason) { + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + triggerEvent(EVENT_MAP.REINDEX_COMPLETE, job); + core.notifications.toasts.addSuccess( + { + title: (( + <> + Source index has been successfully reindexed as{" "} + + + ) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + } else { + let errors: ReactChild[] = []; + if (failures.length) { + errors.push( +
    + {Array.from(new Set(failures.map((item) => item.cause?.reason).filter((item) => item))).map((item) => ( +
  • {item}
  • + ))} +
+ ); + } + + if (error?.reason) { + errors.push( +
    +
  • {error.reason}
  • +
+ ); + } + + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + core.notifications.toasts.addDanger( + { + title: (( + <> + Reindex from to {extras.destIndex} has some errors, please check the errors + below: + + ) as unknown) as string, + text: ((
{errors}
) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + } + return true; + } + } + + return false; + }, + timeoutCallback(job: ReindexJobMetaData) { + const extras = job.extras; + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + core.notifications.toasts.addDanger( + { + title: (( + <> + Reindex from to {extras.destIndex} does not finish in reasonable time, please check + the task {extras.taskId} manually + + ) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + }, + listenType: "reindex", + }); + jobSchedulerInstance.addCallback({ + callbackName: "callbackForSplit", + callback: async (job: RecoveryJobMetaData) => { + const extras = job.extras; + const indexResult = await indexService.getIndices({ + from: 0, + size: 10, + search: extras.destIndex, + terms: extras.destIndex, + sortField: "index", + sortDirection: "desc", + showDataStreams: false, + }); + if (indexResult.ok) { + const [firstItem] = indexResult.response.indices || []; + if (firstItem && firstItem.health !== "red") { + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + triggerEvent(EVENT_MAP.SPLIT_COMPLETE, job); + core.notifications.toasts.addSuccess( + { + title: (( + <> + Source index has been successfully split as{" "} + . + + ) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + return true; + } + } + + return false; + }, + timeoutCallback(job: RecoveryJobMetaData) { + const extras = job.extras; + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + core.notifications.toasts.addDanger( + { + title: (( + <> + Split to {extras.destIndex} does not finish in reasonable time, please check the + index manually + + ) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + }, + listenType: "split", + }); + jobSchedulerInstance.addCallback({ + callbackName: "callbackForShrink", + callback: async (job: RecoveryJobMetaData) => { + const extras = job.extras; + const indexResult = await indexService.getIndices({ + from: 0, + size: 10, + search: extras.destIndex, + terms: extras.destIndex, + sortField: "index", + sortDirection: "desc", + showDataStreams: false, + }); + if (indexResult.ok) { + const [firstItem] = indexResult.response.indices || []; + if (firstItem && firstItem.health !== "red") { + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + triggerEvent(EVENT_MAP.SHRINK_COMPLETE, job); + core.notifications.toasts.addSuccess( + { + title: (( + <> + Source index has been successfully shrunken as{" "} + . + + ) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + return true; + } + } + + return false; + }, + timeoutCallback(job: RecoveryJobMetaData) { + const extras = job.extras; + if (extras.toastId) { + core.notifications.toasts.remove(extras.toastId); + } + core.notifications.toasts.addDanger( + { + title: (( + <> + Shrink to {extras.destIndex} does not finish in reasonable time, please check the + index manually. + + ) as unknown) as string, + }, + { + toastLifeTimeMs: 1000 * 60 * 60 * 24 * 5, + } + ); + }, + listenType: "shrink", + }); +}