punchcard-cms/punchcard

View on GitHub
lib/api/apis.js

Summary

Maintainability
A
3 hrs
Test Coverage
'use strict';

const _ = require('lodash');
const Promise = require('bluebird');

const utils = require('./utils');
const follow = require('./follow');
const database = require('../database');

/*
 * Determines Content Types and Content Type Keys
 *
 * @param {object} app - An Express app
 *
 * @returns {object} types - An object containing `key` of Content Type keys and `types` of all Content Types
 */
const types = app => {
  let cts = _.cloneDeep(app.get('content-types')).map(ct => {
    const type = {};

    Object.keys(ct).forEach(attr => {
      if (attr !== 'attributes') {
        type[attr] = ct[attr];
      }
    });

    return type;
  });
  const ctKeys = Object.keys(cts[0]).sort().filter(key => {
    if (key === 'name') {
      return false;
    }

    return true;
  });
  ctKeys.unshift('name');

  cts = cts.map(ct => {
    const type = ct;
    type.meta = {
      url: `/api/types/${ct.id}`,
    };

    delete type.workflow;
    delete type.identifier;

    return type;
  });

  return {
    keys: ctKeys,
    all: cts,
  };
};

/*
 *  API for all Content
 *
 * @param {object} query - Request queries
 * @param {array} models - Object of all content types
 *
 * @returns {object} - All items and the pagination for the items
 */
// TODO: Return error if no items are found
const all = (query, models, next) => {
  const organize = utils.organize(query);
  const mods = _.cloneDeep(models) || [];

  return database.transaction(trx => {
    const results = trx('live').select('*');

    if (query.key) {
      results.where({
        key: query.key,
      });
    }
    else if (query.key_slug) { // eslint-disable-line camelcase
      results.where({
        'key-slug': query.key_slug, // eslint-disable-line camelcase
      });
    }

    return results.orderBy(organize.sort.by, organize.sort.dir).offset(organize.page.offset).limit(organize.page.limit).then(rows => {
      return trx('live').count('id').then(total => {
        if (query.follow) {
          return follow(query, rows, mods, total, organize, trx);
        }

        return utils.format(rows, '', mods, query, trx).then(formatted => {
          return {
            items: formatted,
            pages: utils.page('api', organize, total[0].count),
          };
        });
      })
      .catch(e => {
        const err = {
          message: e.message,
          safe: '/',
          status: 500,
        };

        return next(err);
      });
    });
  });
};

/*
 *  API for all Content Types
 *
 * @param {object} query - Request queries
 * @param {object} cts - Content types
 *
 * @returns {object} - All items and the pagination for the items
 */
// TODO: Return error if no items are found
const content = (query, cts) => {
  const organize = utils.organize(query, cts.keys);
  const pages = utils.page('api/types', organize, cts.all.length);

  const items = _.sortBy(cts.all, o => {
    return o[organize.sort.by];
  });

  if (organize.sort.dir === 'desc') {
    items.reverse();
  }

  const results = _.cloneDeep(items).slice(organize.page.offset, organize.page.offset + organize.page.limit);

  return Promise.map(results, ct => {
    return database('live').count('id').where({
      'type-slug': ct.id,
    }).then(total => {
      const type = ct;
      type.meta.count = parseInt(total[0].count, 10);

      return type;
    });
  }).then(formatted => {
    return {
      items: formatted,
      pages,
    };
  });
};

/*
 *  API for all Content of a given Type
 *
 * @param {object} query - Request queries
 * @param {object} model - The content type model
 * @param {array} models - Object of all content types
 *
 * @returns {object} - All items and the pagination for the items
 */
// TODO: Return error if no items are found
const ofType = (query, model, models) => {
  const organize = utils.organize(query);
  const mods = _.cloneDeep(models) || [];
  let attrs = [];

  if (model.hasOwnProperty('attributes')) {
    attrs = model.attributes;
  }

  const where = {
    'type-slug': model.id,
  };

  if (query.key) {
    where.key = query.key;
  }
  else if (query.key_slug) { // eslint-disable-line camelcase
    where['key-slug'] = query.key_slug; // eslint-disable-line camelcase
  }

  return database.transaction(trx => {
    return trx('live').select('*').where(where).orderBy(organize.sort.by, organize.sort.dir).offset(organize.page.offset).limit(organize.page.limit).then(rows => {
      return trx('live').where({
        'type-slug': model.id,
      }).count('id').then(total => {
        if (query.follow) {
          return follow(query, rows, mods, total, organize, trx);
        }

        return utils.format(rows, attrs, mods, query, trx).then(formatted => {
          return {
            items: formatted,
            pages: utils.page('api', organize, total[0].count),
          };
        });
      });
    });
  });
};

/*
 *  A Specific Piece of Content
 *
 * @param {object} query - Request queries
 * @param {string} id - The GUID to search
 * @param {array} model - Array of content type attributes
 *
 * @returns {object} - All items and the pagination for the items
 */
// TODO: Return error if no items are found
const one = (query, id, model, models) => {
  const params = _.cloneDeep(query) || {};
  params.follow = true;

  return database.transaction(trx => {
    return utils.one(params, id, model, models, trx);
  });
};

module.exports = {
  types,
  all,
  content,
  ofType,
  one,
};