Strider-CD/strider

View on GitHub
apps/strider/lib/routes/projects.js

Summary

Maintainability
F
1 wk
Test Coverage
const async = require('async');
const common = require('../common');
const debug = require('debug')('strider:routes:projects');
const express = require('express');
const models = require('../models');
const pjson = require('../../package.json');
const router = express.Router();
const Project = models.Project;
const User = models.User;

router
  .route('/')
  .get(function (req, res) {
    return renderProjects(false, req, res);
  })
  .post(function (req, res) {
    const refresh = req.body.refresh !== 'false' && req.body.refresh !== '0';

    return renderProjects(refresh, req, res);
  });

module.exports = router;

function renderProjects(refresh, req, res) {
  const tasks = [];
  const repomap = {};
  const configured = {};
  const unconfigured = [];
  const providers = common.userConfigs.provider;
  const manual = {};
  const manualProjects = {};

  Object.keys(common.pluginConfigs.provider).forEach(function (key) {
    const config = common.pluginConfigs.provider[key];
    if (common.extensions.provider[key].hosted) return;
    manualProjects[key] = [];
    manual[key] = {
      provider: config,
      projects: manualProjects[key],
    };
  });

  Project.find({ creator: req.user._id })
    .lean()
    .exec(function (err, projects) {
      if (err) return res.send(500, 'Failed to get projects from the database');
      // tree is { providerid: { accountid: { repoid: project._id, ...}, ...}, ...}
      // to track which repos have been configured
      const tree = {};
      for (let i = 0; i < projects.length; i++) {
        const provider = projects[i].provider;
        if (!provider.account) {
          if (!manual[provider.id]) {
            manual[provider.id] = {
              provider: common.pluginConfigs.provider[provider.id],
              projects: [],
            };
          }
          manual[provider.id].projects.push(projects[i]);
          continue;
        }
        deepObj(tree, provider.id, provider.account)[provider.repo_id] = {
          _id: projects[i]._id,
          name: projects[i].name,
        };
      }

      req.user.accounts.forEach(function (account) {
        configured[account.provider] = true;

        // Caching
        const haveCache =
          Array.isArray(account.cache) && account.cache.length > 0;
        if (!refresh && haveCache) {
          groupRepos(account, repomap, tree, account.toJSON().cache);
          return;
        }

        tasks.push(function (next) {
          debug(
            `Now trying to list repos for the following provider for an account in the user model: ${account.provider}`
          );

          if (!common.extensions.provider[account.provider]) {
            next(
              new Error(
                `The plugin for ${account.provider} could not be found. Please install the plugin from Admin > Plugins`
              )
            );
          } else {
            common.extensions.provider[account.provider].listRepos(
              account.config,
              function (err, repos) {
                if (err) {
                  if (haveCache) {
                    groupRepos(account, repomap, tree, account.toJSON().cache);
                  }
                  return next(err);
                }
                account.set('cache', repos);
                groupRepos(account, repomap, tree, repos);
                account.last_updated = new Date();
                next();
              }
            );
          }
        });
      });
      for (const id in providers) {
        if (configured[id] || !providers[id].setupLink) continue;
        unconfigured.push(providers[id]);
      }
      debug('Fetching projects...');
      async.parallel(tasks, function (err) {
        if (err) {
          req.flash(
            'account',
            `Failed to refresh repositories: ${err.message || err}`
          );
          debug(err);
        }
        // cache the fetched repos
        User.updateOne(
          { _id: req.user._id },
          { $set: { accounts: req.user.toJSON().accounts } },
          function (err, num) {
            if (err) debug('error saving repo cache');
            if (!num) debug("Didn't effect any users");
            debug('Saved repo cache');
            // user is already be available via the "currentUser" template variable

            return res.format({
              html: function () {
                res.render('projects.html', {
                  unconfigured: unconfigured,
                  providers: providers,
                  manual: manual,
                  manualProjects: manualProjects,
                  repos: repomap,
                  flash: req.flash(),
                  project_types: availableProjectTypes(),
                  version: pjson.version,
                });
              },
              json: function () {
                res.send({
                  unconfigured: unconfigured,
                  providers: providers,
                  manual: manual,
                  manualProjects: manualProjects,
                  repos: repomap,
                  project_types: availableProjectTypes(),
                });
              },
            });
          }
        );
      });
    });
}

function availableProjectTypes() {
  const available = {};
  for (const id in common.project_types) {
    let good = true;
    const plugins = common.project_types[id].plugins;
    for (let i = 0; i < plugins.length; i++) {
      if (!common.extensions.job[plugins[i]]) {
        good = false;
        break;
      }
    }
    if (good) {
      available[id] = common.project_types[id];
    }
  }
  return available;
}

function groupRepos(account, repomap, tree, repos) {
  const groups = deepObj(repomap, account.provider, account.id);
  const projectmap = getDeep(tree, account.provider, account.id) || {};
  for (let i = 0; i < repos.length; i++) {
    if (!groups[repos[i].group]) {
      groups[repos[i].group] = {
        configured: 0,
        repos: [],
      };
    }
    repos[i].project = projectmap[repos[i].id];
    groups[repos[i].group].repos.push(repos[i]);
    if (repos[i].project) {
      groups[repos[i].group].configured += 1;
    }
  }
}

function getDeep(obj) {
  return [].slice.call(arguments, 1).reduce(function (obj, name) {
    return obj && obj[name];
  }, obj);
}

function deepObj(obj) {
  const names = [].slice.call(arguments, 1);
  return names.reduce(function (obj, name) {
    return obj[name] || (obj[name] = {});
  }, obj);
}