From c05502f15cfdf5f2dae3c376c90a73184746694d Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 9 Aug 2022 14:07:53 +0000 Subject: [PATCH] Super Pipelines and display --- lib/jobs/job-builder.js | 86 ++++++++++++++------------- lib/sockets/clients/Initiator.js | 3 +- src/ctx/JobContext.jsx | 53 +++++++++++------ src/views/jobs/JobPipelineBox.jsx | 50 ++++++++++++++++ src/views/jobs/JobPipelineDisplay.jsx | 63 ++++++++++++++++++++ src/views/jobs/JobView.jsx | 3 + src/views/jobs/Jobs.jsx | 26 +++++--- tests/index.js | 14 +++-- 8 files changed, 223 insertions(+), 75 deletions(-) create mode 100644 src/views/jobs/JobPipelineBox.jsx create mode 100644 src/views/jobs/JobPipelineDisplay.jsx diff --git a/lib/jobs/job-builder.js b/lib/jobs/job-builder.js index f752bcc..9df5c47 100644 --- a/lib/jobs/job-builder.js +++ b/lib/jobs/job-builder.js @@ -8,58 +8,60 @@ const pipelineMapping = [ ]; const buildCommon = (jobRequest) => { - const { testName } = jobRequest; - if (!testName) throw Error("'testName' must be provided!"); - const command = [baseCommand, suiteEntry, `test=${testName}`]; - + const { isTriage, ignore, region, testNames } = jobRequest; + const command = [baseCommand, suiteEntry]; // Apply Common Flags - command.push("isRetry=false"); - + command.push(`isTriage=${isTriage}`); + if(ignore && ignore.length > 0) console.log("Would ignore", ignore); + if(region) + command.push(`region=${region}`); + // Return new request return { ...jobRequest, command }; }; -const buildSingle = (jobReq) => jobReq; - -const buildMarker = (jobReq) => {}; - -const buildProject = (jobReq) => {}; - -const pipelineMaxLife = (testName) => { - const pipelines = pipelineMapping - .filter((m) => m.pipeline.find((t) => t.name === testName)) - .map((m) => m.pipeline); - return Math.max(pipelines.map((p) => p.length)) + 1; -}; - -const buildCompound = (jobReq, socketId) => { - const { testName, command } = jobReq; - const { pipeline } = jobReq; - if (pipeline) { - pipeline.dashboardSocketId = socketId; - const pipelineArg = Buffer.from(JSON.stringify(pipeline), "utf8").toString( - "base64" - ); - command.push(`pipeline=${pipelineArg}`); - } +const buildManual = (jobReq) => { + const {testNames} = jobReq; + if(testNames.length > 1) throw Error("Currently only 1 test can be selected!"); + command.push(`test=${testNames[0]}`); + return { ...jobReq, command }; }; -function nextCompound(previousTest) {} +const buildTags = (jobReq) => { + const {command, tags, testNames} = jobReq; + if(testNames && testNames.length > 0){ + return console.log("Would run tags as manual"); + } + const arg = Buffer.from(JSON.stringify(tags), "utf8").toString( + "base64" + ); + command.push(`tags=${arg}`); + return {...jobReq, command}; +}; + +const buildPipeline = (jobReq, socketId) => { + const { command, pipeline } = jobReq; + const {__test: test} = pipeline; + if(!test) throw Error("__test is required for pipeline jobs!"); + pipeline.dashboardSocketId = socketId; + const arg = Buffer.from(JSON.stringify(pipeline), "utf8").toString( + "base64" + ); + command.push(`pipeline=${arg}`); + command.push(`test=${test}`); + return { ...jobReq, command }; +}; export default function jobBuilder(jobRequest, id) { const jobReq = buildCommon(jobRequest, id); - switch (jobRequest.type) { - case "single": - return buildSingle(jobReq); - case "marker": - return buildMarker(jobReq); - case "project": - return buildProject(jobReq); - case "compound": - return buildCompound(jobReq, id); - default: - throw Error("No Job Request Type Specified!"); - } + const {pipeline, testNames, tags } = jobReq; + if(pipeline) + return buildPipeline(jobReq, id); + else if(tags) + return buildTags(jobReq); + else if(testNames) + return buildManual(jobReq); //TODO currently does nothing + else throw Error("At least 1 'pipeline or tags or testNames' is required! "); } diff --git a/lib/sockets/clients/Initiator.js b/lib/sockets/clients/Initiator.js index b2bb2de..c0aca30 100644 --- a/lib/sockets/clients/Initiator.js +++ b/lib/sockets/clients/Initiator.js @@ -64,8 +64,7 @@ export default class Initiator { delete triggers[testName].__testDelay; const jobReq = { ...jobRequest, - pipeline: { ...pipeline, triggers: triggers[testName] }, - testName, + pipeline: {...pipeline, triggers: triggers[testName],__test:testName }, }; setTimeout( () => diff --git a/src/ctx/JobContext.jsx b/src/ctx/JobContext.jsx index 15d5cb7..1bb3df3 100644 --- a/src/ctx/JobContext.jsx +++ b/src/ctx/JobContext.jsx @@ -25,14 +25,6 @@ const initialState = { pipelines: [], }; -/* -pipelines: [{tracks:[ -{ - -} -]}] -*/ - const reducer = (state, action) => { // Current Jobs const { jobs, pipelines } = state; @@ -80,7 +72,7 @@ export const JobProvider = ({ children }) => { return jobFactory({ testNames: ["single"] }); } - function pipelineComponentJob(jobPipeline, testName) { + function pipelineComponentJob(jobPipeline, pipelineReq) { const i = new Initiator(url); const jobId = `j${Date.now()}`; const job = { @@ -88,15 +80,14 @@ export const JobProvider = ({ children }) => { status: jobStatus.PENDING, jobId, isPipeline: true, - builderCache: {}, initiator: i, + pipelineId: jobPipeline.id, + branchId: pipelineReq.pipeline.__test }; const request = { - testName, image: "node", - type: "pipeline", name: jobId, - pipelineTriggers: "secondary", + pipeline: pipelineReq, }; jobCreate(job); @@ -113,8 +104,26 @@ export const JobProvider = ({ children }) => { job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR; jobUpdate({ ...job }, jobId); }; - const onPipelineTrigger = (trigger) => { - console.log("Got trigger", trigger); + const onPipelineTrigger = (p) => { + const { triggers } = p; + for (var t in triggers) { + const delay = triggers[t].__testDelay ?? 0; + delete triggers[t].__testDelay; + const jobReq = { + ...request, + pipeline: { + ...pipeline, + triggers: triggers[t],__test:t + }, + }; + jobPipeline.pendingTriggers[t].push( + { + + testName: t, + timer: setTimeout(() => pipelineComponentJob(jobPipeline, jobReq),delay), + triggerAt: Date.now() + delay + }); + } }; const started = i.newPipelineJob( request, @@ -127,10 +136,16 @@ export const JobProvider = ({ children }) => { function pipelineFactory(builderCache) { console.log("Would create pipeline with cache"); - console.log(builderCache); - return pipelineComponentJob(state.pipelines, builderCache.branches[0][0]); - // return jobId; - /*return jobFactory({testNames: ["primary"]});*/ + const { tree, branches } = builderCache; + const __test = Object.keys(tree)[0]; + const pipelineReq = {image: "node", pipeline:{__test, triggers: { ...tree[__test] }}} + const id = `pij${Date.now()}`; + const pipeline = {id, branches, pendingTriggers: {} }; + const {pipelines} = state; + pipelines.push(pipeline); + updatePipelines([...pipelines]); + return pipelineComponentJob(pipeline, pipelineReq); + } function jobCancel(jobId) { diff --git a/src/views/jobs/JobPipelineBox.jsx b/src/views/jobs/JobPipelineBox.jsx new file mode 100644 index 0000000..b05b262 --- /dev/null +++ b/src/views/jobs/JobPipelineBox.jsx @@ -0,0 +1,50 @@ +import React, { useState, useContext } from "react"; +import StoreContext from "../../ctx/StoreContext.jsx"; +import JobContext, { jobStatus } from "../../ctx/JobContext.jsx"; + +import Accordion from "@mui/material/Accordion"; +import AccordionDetails from "@mui/material/AccordionDetails"; +import AccordionSummary from "@mui/material/AccordionSummary"; +import Typography from "@mui/material/Typography"; + +import IconButton from "@mui/material/IconButton"; +import CheckIcon from "@mui/icons-material/Check"; +import ClearIcon from "@mui/icons-material/Clear"; +import ViewColumnIcon from "@mui/icons-material/ViewColumn"; +import PendingIcon from "@mui/icons-material/Pending"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb"; + +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; + +export default function JobPipelineBox(props) { + const { pipeline } = props; + + function jobIcon() { + return ; + } + + return ( + + + + {pipeline.id} + + + + {jobIcon()} + + + + + ); +} diff --git a/src/views/jobs/JobPipelineDisplay.jsx b/src/views/jobs/JobPipelineDisplay.jsx new file mode 100644 index 0000000..ff33eb8 --- /dev/null +++ b/src/views/jobs/JobPipelineDisplay.jsx @@ -0,0 +1,63 @@ +import React, { useContext } from "react"; +import {useNavigate} from "react-router-dom"; +import JobContext from "../../ctx/JobContext.jsx"; +import Button from "@mui/material/Button"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; + +import Accordion from "@mui/material/Accordion"; +import AccordionSummary from "@mui/material/AccordionSummary"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import Stack from "@mui/material/Stack"; + +function JobPipelineDisplay(props) { + const { back, pipeline } = props; + const {state: jobState} = useContext(JobContext); + const navigate = useNavigate(); + const pipelineJobs = jobState.jobs.filter((j)=>j.isPipeline && j.pipelineId === pipeline.id); + + const selectJob = (testName) => () =>{ + const job = pipelineJobs.find((j)=>j.branchId === testName); + if(!job) return; navigate(`/qualiteer/jobs#${job.jobId}`); + } + + return ( + +

{}

+ {pipeline.branches.map((track, i) => ( + + {i + 1} + + {track.map((test, j) => ( + + + + {test} + + I + + + ))} + + + ))} +
+ ); +} + +export default JobPipelineDisplay; diff --git a/src/views/jobs/JobView.jsx b/src/views/jobs/JobView.jsx index d7dc8fb..55182e1 100644 --- a/src/views/jobs/JobView.jsx +++ b/src/views/jobs/JobView.jsx @@ -72,7 +72,10 @@ export default function JobView(props) { cb(); }; + + function navigateToJobs() { + if(job.isPipeline) return navigate(`/qualiteer/jobs#p${job.pipelineId}`) navigate("/qualiteer/jobs"); } diff --git a/src/views/jobs/Jobs.jsx b/src/views/jobs/Jobs.jsx index 0571fd1..4e260c9 100644 --- a/src/views/jobs/Jobs.jsx +++ b/src/views/jobs/Jobs.jsx @@ -3,7 +3,9 @@ import { useLocation, useNavigate } from "react-router-dom"; import JobContext from "../../ctx/JobContext.jsx"; import JobBox from "./JobBox.jsx"; +import JobPipelineBox from "./JobPipelineBox.jsx"; import JobView from "./JobView.jsx"; +import JobPipelineDisplay from "./JobPipelineDisplay.jsx"; import JobBuilder from "./builder/JobBuilder.jsx"; import Typography from "@mui/material/Typography"; import Box from "@mui/material/Box"; @@ -15,7 +17,11 @@ export default function Jobs() { useEffect(() => { const jobName = location.hash.slice(1); - if (!jobName || jobState.jobs.find((job) => job.name === jobName)) return; + const pipelineId = jobName.slice(1); + const noPipeline = !jobName || jobState.pipelines.find((p)=>p.id ===pipelineId); + + const noJob = !jobName || jobState.jobs.find((job) => job.name === jobName) + if(!noPipeline || !noJob) return; navigate("/qualiteer/jobs"); }); @@ -45,8 +51,9 @@ export default function Jobs() { ) : null} - {location.hash === "" && - jobState.jobs + {location.hash === "" &&( + + {jobState.jobs .filter((j) => !j.isPipeline) .map((v, i) => ( - - ))} - {jobState.jobs.find((job) => job.name === location.hash.slice(1)) && ( - ))} + {jobState.pipelines.map((p,i)=> + + )} + ) + } + + { location.hash[1] === "p"? p.id===location.hash.slice(2))}/> : + jobState.jobs.find((job) => job.name === location.hash.slice(1)) && ( job.name === location.hash.slice(1))} /> )} diff --git a/tests/index.js b/tests/index.js index aaf717c..f5be35d 100644 --- a/tests/index.js +++ b/tests/index.js @@ -12,16 +12,20 @@ const url = process.env.QUALITEER_URL; // Create an initiator and make a job request const primary = new Initiator(url); const job = { - type: "compound", - testName: "primary", pipeline: { + __test: "primary", triggers: { secondary1: { - tertiary1: {}, - tertiary2: { __testDelay: 5000 }, __testDelay: 1000, + tertiary1: {}, + tertiary2: { + __testDelay: 8000 }, + }, + secondary2: { + __testDelay: 20000, + tertiary3: { + __testDelay: 3000 }, }, - secondary2: { tertiary3: { __testDelay: 3000 }, __testDelay: 15000 }, }, }, name: "testing",