Strider-CD/strider

View on GitHub
apps/strider/lib/routes/jobs/index.js

Summary

Maintainability
F
1 wk
Test Coverage
/*
 * routes/jobs/index.js
 */

const _ = require('lodash');
const common = require('../../common');
const config = require('../../config');
const debug = require('debug')('strider:routes:jobs');
const ljobs = require('../../jobs');
const models = require('../../models');
const pjson = require('../../../package.json');
const filter = require('../../utils/ansi');
const utils = require('../../utils');

const Job = models.Job;

module.exports = {
  html: html,
  multijob: multijob,
  jobs: jobs,
};

/*
 * GET /org/repo/[job/:job_id] - view latest build for repo
 *
 * middleware.project set "project" and "accessLevel" on the req object.
 */
function multijob(req, res) {
  const type = req.accepts('html', 'json', 'plain');
  switch (type) {
    case 'json':
      return data(req, res);
    case 'plain':
      return output(req, res);
    default:
      return html(req, res);
  }
}

function filterJob(job) {
  if (job.trigger.message === 'Retest') {
    job.trigger.message = 'Manually Retested';
  }
  if (job.trigger.message === 'Redeploy') {
    job.trigger.message = 'Manually Redeployed';
  }
  return job;
}

function findJob(job) {
  // job.runner can be undefined if it hasn't been fully prepared yet.
  // this is a sort of race between job.prepare and job.new events.
  // fixes https://github.com/Strider-CD/strider/issues/273
  if (!job.runner) return;

  const runner = common.extensions.runner[job.runner.id];
  if (runner) return runner.getJobData(job._id) || {};
}

function html(req, res, next) {
  if (req.params.org === 'auth') {
    return next();
  }

  const id = req.params.id;
  const projectName = req.project.name;
  const jobsQuantity = req.user
    ? req.user.jobsQuantityOnPage
    : config.jobsQuantityOnPage.default;

  Job.find({ project: projectName, archived: null })
    .sort({ finished: -1 })
    .limit(jobsQuantity)
    .lean()
    .exec(function (err, jobs) {
      if (err) {
        debug('[job] error finding jobs', err.message);
        return res.status(500).send('Failed to find jobs');
      }
      // Use our custom sort function
      jobs.sort(ljobs.sort);

      Job.find({
        project: projectName,
        archived: null,
        finished: null,
      })
        .sort({ started: -1 })
        .lean()
        .exec(function (err, running) {
          if (err) {
            debug('[job] error finding running jobs', err.message);
            return res.status(500).send('Failed to find running jobs');
          }
          let i;
          for (i = 0; i < running.length; i++) {
            _.extend(running[i], findJob(running[i]));
            delete running[i].data;
            delete running[i].id;
          }
          jobs = running.concat(jobs);

          const showStatus = {};
          const sanitized = utils.sanitizeProject(req.project);
          sanitized.access_level = req.accessLevel;
          req.project.branches.forEach(function (branch) {
            const plugins = (showStatus[branch.name] = {});
            branch.plugins.forEach(function (plugin) {
              plugins[plugin.id] = plugin.enabled && plugin.showStatus;
            });
          });

          let job = id ? null : jobs[0];
          for (i = 0; i < jobs.length; i++) {
            if (!job && jobs[i]._id === id) job = jobs[i];
            jobs[i] = ljobs.small(jobs[i]);
            jobs[i] = filterJob(jobs[i]);
            jobs[i].project = sanitized;
          }
          if (job) {
            job.status = ljobs.status(job);
            job.project = sanitized;
          }

          const isGlobalAdmin = req.user && req.user.account_level > 0;
          const canAdminProject = sanitized.access_level > 0 || isGlobalAdmin;

          // Make sure jobs are only listed once.
          jobs = _.uniqBy(jobs, (job) => job._id.toString());

          res.format({
            html: function () {
              debug(
                'Build page requested. Logging jobs to investigate duplicate job listings.'
              );
              debug(jobs);
              res.render('build.html', {
                project: sanitized,
                accessLevel: req.accessLevel,
                canAdminProject: canAdminProject,
                jobs: jobs,
                job: job,
                statusBlocks: common.statusBlocks,
                showStatus: showStatus,
                page_base: `${req.params.org}/${req.params.repo}`,
                version: pjson.version,
              });
            },
            json: function () {
              res.send({
                project: sanitized,
                accessLevel: req.accessLevel,
                canAdminProject: canAdminProject,
                jobs: jobs,
                job: job,
              });
            },
          });
        });
    });
}

function getJob(req, res, next) {
  let query;
  if (!req.params.job_id) {
    query = Job.findOne(
      { project: req.project.name.toLowerCase(), archived: null },
      {},
      { sort: { finished: -1 } }
    );
  } else {
    query = Job.findOne({
      project: req.project.name.toLowerCase(),
      _id: req.params.job_id,
      archived: null,
    });
  }
  query.exec(function (err, job) {
    if (err || !job) return res.status(404).send('Failed to find job');
    job = filterJob(job);
    if (!job.finished) {
      _.extend(job, findJob(job));
    }
    next(job);
  });
}

function output(req, res) {
  getJob(req, res, function (job) {
    res.setHeader('Content-type', 'text/plain');
    res.send(job.std.merged ? filter(job.std.merged) : '');
  });
}

function data(req, res) {
  getJob(req, res, function (job) {
    res.setHeader('Content-type', 'application/json');
    res.send(job);
  });
}

function jobs(req, res) {
  Job.find({ project: req.project.name.toLowerCase(), archived: null })
    .sort({ finished: -1 })
    .limit(20)
    .lean()
    .exec(function (err, jobs) {
      if (err) return res.status(500).send('Failed to retrieve jobs');
      res.send(
        JSON.stringify(
          jobs.map(function (j) {
            return filterJob(j);
          })
        )
      );
    });
}