Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chunked v2 #4

Merged
merged 66 commits into from
Jun 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
91b53dd
Incremented patch version number
supernomad Jun 19, 2015
24137ab
Added node-cache dependancy for the local cache implementation
supernomad Jun 19, 2015
666ac16
Created the localCache and added some unittests
supernomad Jun 19, 2015
31ae96d
Incremented version number
supernomad Jun 20, 2015
7f28cf5
Added validation and testing for said validation
supernomad Jun 20, 2015
8e4a3d8
Finished up the chunked upload api routes
supernomad Jun 20, 2015
7ebd3dd
updated some nomenclature
supernomad Jun 20, 2015
b565b91
Added error handler to the chunked upload routes
supernomad Jun 20, 2015
cbeea36
Updated test directory to have better organization
supernomad Jun 20, 2015
8bac960
Added supertest dependancy for integration testing the different api's
supernomad Jun 20, 2015
5b05265
Added mockery dependancy for mocking require statements for certain u…
supernomad Jun 20, 2015
4bf3053
Revmoved a setup script for the unit tests as it was unneeded
supernomad Jun 20, 2015
2175455
Added sinon dependancy as a mocking framework
supernomad Jun 20, 2015
2bfc617
Added full unit-testing of the chunked-upload-routes library and fixe…
supernomad Jun 20, 2015
e41c81d
removed some unused mocked calls
supernomad Jun 20, 2015
a39cd7d
trying to fix an odd bug
supernomad Jun 21, 2015
0e71753
fixed a bug in the doesExist function of typeHelper
supernomad Jun 21, 2015
1f579f2
fixed a fatal bug
supernomad Jun 21, 2015
41f7414
fixed a typo
supernomad Jun 21, 2015
13dfb37
Fixed up some more in the chunked-upload-routes and requisite tests
supernomad Jun 21, 2015
c0a75d7
Fixed some more unit-tests
supernomad Jun 21, 2015
fafc665
removed a debuging call to console.log
supernomad Jun 21, 2015
fc05202
Made the unit-tests a bit more complete
supernomad Jun 21, 2015
86e5c96
added ability to turn on and off the localCache mock and update some …
supernomad Jun 21, 2015
d416ac5
fixed up the loaclCache mock so that changing the return value worked…
supernomad Jun 21, 2015
c73f574
updated some model naming
supernomad Jun 21, 2015
71cec10
Created chunked download api.
supernomad Jun 21, 2015
c700010
Added missing commit
supernomad Jun 21, 2015
bba7741
Added unit-tests around chunked-download-validators
supernomad Jun 21, 2015
311306b
Made a few minor bug fixes
supernomad Jun 21, 2015
2ec0c06
added unit-tests for the error handler of the chunked-download-routes
supernomad Jun 21, 2015
1dadc48
Added ReadFileChunk to the io-mock and added more unit-tests around c…
supernomad Jun 21, 2015
2535902
added a few more unit-tests
supernomad Jun 21, 2015
6141da1
Fixed a typo in the unit-tests
supernomad Jun 21, 2015
9c9fcff
created the express router wrapper library for the chunked-api
supernomad Jun 24, 2015
e0276fc
started refactoring the libraries and tests to utilize the async module
supernomad Jun 24, 2015
5fb8c04
Moved the unit-tests for the chunked-upload-routes to utilize the asy…
supernomad Jun 25, 2015
320c155
added some more unit-tests around cache failure in the chunked-upload…
supernomad Jun 25, 2015
3a1dcbf
fixed a bug in the unit-tests for the chunked-upload-routes
supernomad Jun 25, 2015
4c5d476
Added codacy badge
supernomad Jun 26, 2015
084dc68
fixed a potential bug in the typeHelper library
supernomad Jun 26, 2015
b894b64
Fixed a potential bug in the guidHelper
supernomad Jun 27, 2015
49bed52
Made the quote usage consistent
supernomad Jun 27, 2015
fd6d198
moved the chunked-download-routes to use the async module as well as …
supernomad Jun 28, 2015
9912743
added codacy coverage to test compared to coveralls
supernomad Jun 28, 2015
f7db1cf
added missing load task in grunt
supernomad Jun 28, 2015
38f60e8
removed coveralls task as it was failing the build due to server prob…
supernomad Jun 28, 2015
95b9d29
removed the load event as well
supernomad Jun 28, 2015
0bcd64e
Trying to get the codacy plugin to work
supernomad Jun 28, 2015
75e46f7
added source lcov file for coverage
supernomad Jun 28, 2015
8f1ca88
removed repo token for codacy from the gruntfile
supernomad Jun 28, 2015
56a2581
fixed some code quality issues
supernomad Jun 28, 2015
4f9c518
updated should package
supernomad Jun 28, 2015
fd72820
fixed up the usage of the should framework
supernomad Jun 28, 2015
c99af97
Fixed a missed call to Number
supernomad Jun 28, 2015
dbb496c
Fixed some typos
supernomad Jun 28, 2015
ab26783
Fixed a bunch of code quality issues
supernomad Jun 28, 2015
4ac42a7
Fixed up some more code quality issues
supernomad Jun 28, 2015
0b6fec4
Added missing semi colon
supernomad Jun 28, 2015
75bc2e9
Fixed some problems with code complexity in the chunked-upload-valida…
supernomad Jun 28, 2015
d4c9b57
added missing chunks
supernomad Jun 28, 2015
995d55f
Fixed a bug where the upload routes were note validating the maximum …
supernomad Jun 28, 2015
60fc9cd
added coveralls back to the gruntfile codacy still needs work to matc…
supernomad Jun 28, 2015
e5490c4
Fixed up some of the unit tests
supernomad Jun 28, 2015
40bc02e
Added missing semi colon
supernomad Jun 28, 2015
c1c9015
Fixed up the last of the unit-tests
supernomad Jun 28, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-mocha-istanbul');
grunt.loadNpmTasks('grunt-coveralls');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-mkdir');
grunt.loadNpmTasks('grunt-mkdir');
grunt.loadNpmTasks('grunt-coveralls');
grunt.loadNpmTasks('grunt-codacy');

grunt.initConfig({
mkdir: {
Expand All @@ -28,9 +29,16 @@ module.exports = function(grunt) {
coverage: {
src: 'coverage/*.info'
}
},
codacy: {
options: {
},
coverage: {
src: 'coverage/lcov.info'
}
}
});

grunt.registerTask('build', ['mkdir:testDir', 'mocha_istanbul:coverage', 'coveralls:coverage']);
grunt.registerTask('build', ['mkdir:testDir', 'mocha_istanbul:coverage', 'coveralls:coverage', 'codacy:coverage']);
grunt.registerTask('default', ['mkdir:testDir', 'mocha_istanbul:coverage']);
};
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[![GitHub license](https://img.shields.io/badge/license-GPL--3.0-green.svg)]()
[![GitHub version](https://badge.fury.io/gh/supernomad%2Fchunky.svg)](http://badge.fury.io/gh/supernomad%2Fchunky)
[![Codacy Badge](https://www.codacy.com/project/badge/92a9c19ff3474abeaaf0e869317da1a3)](https://www.codacy.com/app/csaide/chunky)
[![Coverage Status](https://coveralls.io/repos/Supernomad/chunky/badge.svg?branch=master)](https://coveralls.io/r/Supernomad/chunky?branch=master)
[![Build Status](https://travis-ci.org/Supernomad/chunky.svg?branch=master)](https://travis-ci.org/Supernomad/chunky)
# chunky
Expand Down
54 changes: 54 additions & 0 deletions apis/chunked-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* global __dirname */
var express = require('express'),
router = express.Router();

var multer = require('multer'),
bodyParser = require('body-parser'),
typeHelper = require.main.require('libs/helpers/typeHelper'),
io = require.main.require('libs/io'),
apiCache = null,
uploadRoutes = null,
downloadRoutes = null,
tempChunkPath = __dirname + '/tmp/chunks';

function configure(cache, options) {
if(!typeHelper.isObject(cache)) {
apiCache = require.main.require('libs/caching/localCache');
}
else {
apiCache = cache;
}

if(!typeHelper.isObject(options)) {
options = {};
}

uploadRoutes = require.main.require('routes/chunked-upload-routes')(apiCache, io, options);
downloadRoutes = require.main.require('routes/chunked-download-routes')(apiCache, io, options);

// Request parsing
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({extended: false}));
router.use(multer({
dest: tempChunkPath,
rename: function (fieldname, filename) {
return filename.replace(/\W+/g, '-').toLowerCase() + '-' + fieldname.replace(/\W+/g, '-').toLowerCase();
}
}));

// Upload routes
router.get(uploadRoutes.get.uri, uploadRoutes.get.handler);
router.post(uploadRoutes.post.uri, uploadRoutes.post.handler);
router.put(uploadRoutes.put.uri, uploadRoutes.put.handler);
router.delete(uploadRoutes.delete.uri, uploadRoutes.delete.handler);
router.use(uploadRoutes.error.handler);

// Download routes
router.get(downloadRoutes.get.uri, downloadRoutes.get.handler);
router.post(downloadRoutes.post.uri, downloadRoutes.post.handler);
router.delete(downloadRoutes.delete.uri, downloadRoutes.delete.handler);
router.use(downloadRoutes.error.handler);
return router;
}

module.export = configure;
45 changes: 45 additions & 0 deletions libs/caching/localCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var NodeCache = require('node-cache'),
cache = new NodeCache({ stdTTL: 0, checkperiod: 300, useClones: false }),
typeHelper = require.main.require('libs/helpers/typeHelper');

function create(key, val, ttl, callback) {
if(typeHelper.isFunction(callback)) {
cache.set(key, val, ttl, function (error, success) {
callback(error, success);
});
} else {
return cache.set(key, val, ttl);
}
}

function restore(key, callback) {
if(typeHelper.isFunction(callback)) {
cache.get(key, function (error, value) {
callback(error, {key: key, value: value});
});
} else {
var value = cache.get(key);
return {key: key, value: value};
}
}

function update(key, val, ttl, callback) {
return create(key, val, ttl, callback);
}

function del(key, callback) {
if(typeHelper.isFunction(callback)) {
cache.del(key, function (error, count) {
callback(error, count);
});
} else {
return cache.del(key);
}
}

module.exports = {
'create': create,
'restore': restore,
'update': update,
'delete': del
};
13 changes: 13 additions & 0 deletions libs/helpers/errorHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function genericErrorHandler(error, debug) {
if(error) {
if(debug) {
console.error(error);
}
throw new Error(JSON.stringify(error));
}
}


module.exports = {
genericErrorHandler: genericErrorHandler
};
33 changes: 33 additions & 0 deletions libs/helpers/guidHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* global process */
var typeHelper = require.main.require('libs/helpers/typeHelper');

var nanoToSeconds = function(nano) {
return parseFloat(nano) / 1000000000;
};

var getFloatingTime = function() {
var time = process.hrtime();
var seconds = time[0];
var nanoseconds = time[1];
return parseFloat(seconds) + nanoToSeconds(nanoseconds);
};

function newGuid() {
var d = getFloatingTime();
var guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c==='x' ? r : (r&0x3|0x8)).toString(16);
});
return guid;
}

function isGuid(guid) {
return typeHelper.isString(guid)
&& /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[8,9,a,b][0-9a-f]{3}-[0-9a-f]{12}$/ig.test(guid);
}

module.exports = {
newGuid: newGuid,
isGuid: isGuid
};
14 changes: 9 additions & 5 deletions libs/helpers/stringHelper.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
var typeHelper = require('./typeHelper');
var typeHelper = require.main.require('libs/helpers/typeHelper');

function stripTrailingSlashes(str) {
if(!typeHelper.isString(str)) return str;
if(!typeHelper.isString(str)) {
return str;
}
// Match any trailing slash, both '\' and '/', in any quantity as long as they are the last characters in the string.
return str.replace(/[\\\/]+$/g, "");
return str.replace(/[\\\/]+$/g, '');
}

function stripLeadingSlashes(str) {
if(!typeHelper.isString(str)) return str;
if(!typeHelper.isString(str)) {
return str;
}
// Match any leading slash, both '\' and '/', in any quantity as long as they are the first characters in the string.
return str.replace(/^[\\\/]+/g, "");
return str.replace(/^[\\\/]+/g, '');
}

module.exports = {
Expand Down
10 changes: 5 additions & 5 deletions libs/helpers/typeHelper.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
function doesExist(obj) {
return obj != null && obj != undefined;
return obj !== null && obj !== undefined;
}

function isObject(obj) {
return doesExist(obj) && typeof obj === "object";
return doesExist(obj) && typeof obj === 'object';
}

function isFunction(obj) {
return doesExist(obj) && Object.prototype.toString.call(obj) === '[object Function]';
}

function isString(obj) {
return doesExist(obj) && typeof obj === "string";
return doesExist(obj) && typeof obj === 'string';
}

function isBoolean(obj) {
return doesExist(obj) && typeof obj === "boolean";
return doesExist(obj) && typeof obj === 'boolean';
}

function isNumber(obj) {
return doesExist(obj) && typeof obj === "number";
return doesExist(obj) && !isNaN(obj) && typeof obj === 'number';
}

function isType(obj, typ) {
Expand Down
16 changes: 8 additions & 8 deletions libs/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ function getFileStats(path, callback) {
});
}

function getWriteStream(path, position, type) {
return fs.createWriteStream(path, {
flags: type,
mode: 0666,
start: position
});
}

function createFile(path, buffer, offset, length, callback) {
var stream = getWriteStream(path, 0, 'w');
return stream.write(buffer.slice(offset, offset + length), function(error) {
Expand Down Expand Up @@ -48,14 +56,6 @@ function renameFile(path, newPath, callback) {
});
}

function getWriteStream(path, position, type) {
return fs.createWriteStream(path, {
flags: type,
mode: 0666,
start: position
});
}

module.exports = {
GetFileStats: getFileStats,
CreateFile: createFile,
Expand Down
51 changes: 50 additions & 1 deletion libs/models/apiModels.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,56 @@ function errorHandler(handler) {
self.handler = handler;
}

function apiResponse(api, meta, data) {
var self = this;
self.api = api;
self.meta = meta;
self.data = data;
}

function upload(request) {
var self = this;
self.id = '';
self.fileName = request.fileName;
self.fileSize = request.fileSize;
self.destination = request.destination;
self.tempPath = '';
self.finalPath = '';
self.chunkSize = request.chunkSize;
self.count = request.count;
self.chunks = [];

self.configure = function(id) {
self.id = id;
for (var i = 0; i < self.count; i++) {
self.chunks.push(false);
};
self.tempPath = self.destination + '/' + self.id;
self.finalPath = self.destination + '/' + self.fileName;
};
}

function download(request, size, chunkSize) {
var self = this;
self.id = '';
self.path = request.path;
self.fileSize = size;
self.chunkSize = chunkSize;
self.count = (size / chunkSize) + (size % chunkSize > 0 ? 1 : 0);
self.chunks = [];

self.configure = function(id) {
self.id = id;
for (var i = 0; i < self.count; i++) {
self.chunks.push(false);
}
};
}

module.exports = {
RouteHandler: routeHandler,
ErrorHandler: errorHandler
ErrorHandler: errorHandler,
ApiResponse: apiResponse,
Upload: upload,
Download: download
};
30 changes: 30 additions & 0 deletions libs/models/errorModels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function GenericError(code, error, message) {
var self = this;
self.Code = code;
self.Error = error;
self.Message = message;
}

function uploadMissing() {
return new GenericError(404, "Missing Upload Error", "The upload specified could not be found, please check the supplied id and try again.");
}

function downloadMissing() {
return new GenericError(404, "Missing Download Error", "The download specified could not be found, please check the supplied id and try again.");
}

function serverError() {
return new GenericError(500, "Internal Server Error", "There has been an internal server error, so things are basically blowing up in our datacenter. We will get back to you in a minute but go ahead and try again.");
}

function validationError(msg) {
return new GenericError(400, "Validation Error", msg);
}

module.exports = {
GenericError: GenericError,
UploadMissing: uploadMissing,
DownloadMissing: downloadMissing,
ServerError: serverError,
ValidationError: validationError
};
18 changes: 18 additions & 0 deletions libs/validators/chunked-download-validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var typeHelper = require.main.require('libs/helpers/typeHelper');

var valid = 'valid';

function validateDownloadRequest(downloadRequest) {
if(!typeHelper.isObject(downloadRequest)) {
return 'Download request object was not recieved, or is not an object.';
} else if (!typeHelper.isString(downloadRequest.path)) {
return 'Download path missing or it is not a string.';
} else {
return valid;
}
}

module.exports = {
valid: valid,
validateDownloadRequest: validateDownloadRequest
};
Loading