kzwang/node-git-lfs

View on GitHub
lib/routes/batch.js

Summary

Maintainability
A
3 hrs
Test Coverage
'use strict';

var _ = require('lodash');
var config = require('config');
var wrap = require('co-express');
var parse = require('co-body');

var validate = require('jsonschema').validate;

var Store = require('../store');
var Authenticator = require('../authenticator');

const STORE = Store.getStore(config.get('store.type'), config.get('store.options'));

const AUTHENTICATOR = Authenticator.getAuthenticator(config.get('authenticator.type'), config.get('authenticator.options'));

const BATCH_REQUEST_SCHEMA = require('../../schema/http-v1-batch-request-schema.json');

const PRIVATE_LFS = config.get('private');



/**
 * Process upload object
 *
 * @param {String} user
 * @param {String} repo
 * @param {Object} object
 * @returns {Object}
 */
var handleUploadObject = function* (user, repo, object) {
    var oid = object.oid;
    var size = object.size;

    return {
        oid: oid,
        size: size,
        actions: {
            upload: STORE.getUploadAction(user, repo, oid, size),
            verify: STORE.getVerifyAction(user, repo, oid, size)
        }
    };
};

/**
 * Process download object
 *
 * @param {String} user
 * @param {String} repo
 * @param {Object} object
 * @returns {Object}
 */
var handleDownloadObject = function* (user, repo, object) {
    var oid = object.oid;
    var size = object.size;

    var result = {
        oid: oid,
        size: size
    };

    var exist = yield STORE.exist(user, repo, oid);
    if (exist) {
        result.actions = {
            download: STORE.getDownloadAction(user, repo, oid, size)
        };
    } else {
        result.error = {
            code: 404,
            message: 'Object does not exist on the server'
        };
    }
    return result;
};

/**
 * Process verify object
 *
 * @param {String} user
 * @param {String} repo
 * @param {Object} object
 * @returns {Object}
 */
var handleVerifyObject = function* (user, repo, object) {
    var oid = object.oid;
    var size = object.size;

    return {
        oid: oid,
        size: size,
        actions: {
            verify: STORE.getVerifyAction(user, repo, oid, size)
        }
    };
};



module.exports = function(app) {
    app.post('/:user/:repo/objects/batch', wrap(function* (req, res, next) {
        // validate request body according to JSON Schema
        try {
            var body = yield parse.json(req);
            req.jsonBody = body;
            var valid = validate(body, BATCH_REQUEST_SCHEMA).valid;
            if (!valid) {
                let err = new Error();
                err.status = 422;
                next(err);
            } else {
                next();
            }
        } catch (err) {
            next(err);
        }
    }), wrap(function* (req, res, next) {
        try {
            res.set('Content-Type', 'application/vnd.git-lfs+json');

            var body = req.jsonBody;
            var operation = body.operation;

            // validate operation
            if (operation !== 'upload' && operation !== 'verify' && operation !== 'download') {
                return res.status(422).end();
            }

            let user = req.params.user;
            let repo = req.params.repo;
            let authorization = req.header('Authorization');

            if (PRIVATE_LFS && !authorization) {
                res.set('LFS-Authenticate', 'Basic realm="Git LFS"');
                return res.status(401).end();
            }


            let canRead = yield AUTHENTICATOR.canRead(user, repo, authorization);

            if (!canRead) {
                if (authorization) {
                    return res.status(403).end();
                } else {
                    res.set('LFS-Authenticate', 'Basic realm="Git LFS"');
                    return res.status(401).end();
                }

            }

            // validate objects
            let objects = body.objects;
            let results;
            let func;
            let yields = [];

            switch (operation) {
                case 'upload':
                    func = handleUploadObject;
                    // can Write only need to be checked for upload operation
                    let canWrite = yield AUTHENTICATOR.canWrite(user, repo, authorization);
                    if (!canWrite && authorization) {
                        return res.status(403).end();
                    }
                    break;
                case 'download':
                    func = handleDownloadObject;
                    break;
                case 'verify':
                    func = handleVerifyObject;
                    break;
            }
            _.forEach(objects, function(object) {
                yields.push(func(user, repo, object));
            });

            results = yield yields;

            var response = {
                objects: results
            };
            res.status(200).json(response);
        } catch (err) {
            next(err);
        }

    }));
};