Skip to content

Instantly share code, notes, and snippets.

@thebarndog
Last active August 11, 2016 11:39
Show Gist options
  • Save thebarndog/4f253baecfdc1f42eb30acd3bb1ac9c7 to your computer and use it in GitHub Desktop.
Save thebarndog/4f253baecfdc1f42eb30acd3bb1ac9c7 to your computer and use it in GitHub Desktop.
import mimeTypes from 'mime-types';
import Proto from 'uberproto';
import { parseDataURI } from 'dauria';
import { bufferToHash, fromBuffer } from '../utils/buffer';
export default class Service {
constructor(options) {
if (typeof options === 'undefined') {
throw new Error('blob-service: constructor `options` must be provided');
}
if (typeof options.Model === 'undefined') {
throw new Error('blob-service: constructor `options.Model` must be provided');
}
this.Model = options.Model;
this.id = options.id || 'id';
}
extend(obj) {
return Proto.extend(obj, this);
}
get(id, params) {
return new Promise((resolve, reject) => {
const { response } = params;
if (typeof response === 'undefined') {
throw new Error('blob-service: no response object attached to params. Make sure to use middleware');
}
const readStream = this.Model.createReadStream({ key: id });
readStream.on('error', reject);
readStream.on('open', () => {
readStream.pipe(response);
});
readStream.on('end', () => {
resolve({
[ this.id ]: id
});
});
});
}
create(body) {
return new Promise((resolve, reject) => {
const { uri } = body;
const { buffer, MIME: contentType } = parseDataURI(uri);
const hash = bufferToHash(buffer);
const extension = mimeTypes.extension(contentType);
const id = `${ hash }.${ extension }`;
const writeStream = this.Model.createWriteStream({ key: id });
writeStream.on('error', reject);
fromBuffer(buffer).pipe(writeStream);
writeStream.on('finish', () => {
resolve({
[ this.id ]: id
});
});
});
}
remove(id) {
return new Promise((resolve, reject) => {
this.Model.remove({ key: id }, error => {
if (typeof error === 'undefined') {
resolve(id);
} else {
reject(error);
}
});
});
}
}
import fs from 'fs';
import rimraf from 'rimraf';
import chai, { assert, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import { join } from 'path';
import { BlobService } from '../';
import fsBlobStore from 'fs-blob-store';
import { bufferToHash } from '../../utils/buffer';
import { getBase64DataURI } from 'dauria';
import MockRes from 'mock-res';
chai.should();
chai.use(chaiAsPromised);
describe('blob-store', () => {
const blobStore = fsBlobStore(join(__dirname, 'blobs'));
const service = new BlobService({
Model: blobStore
});
const content = new Buffer('hello world!');
const contentHash = bufferToHash(content);
const contentType = 'text/plain';
const contentUri = getBase64DataURI(content, contentType);
const contentExt = 'txt';
const contentId = `${ contentHash }.${ contentExt }`;
it('creates a blob', () => {
return service.create({ uri: contentUri }, {}).then(blob => blob.id).should.eventually.equal(contentId);
});
it('fetches a blob', () => {
const response = new MockRes();
response.on('finish', () => {
expect(response._getString()).to.equal('hello world!');
});
return service.create({ uri: contentUri }, {})
.then(blob => blob.id)
.then(id => service.get(id, { response }))
.then(blob => blob.id).should.eventually.equal(contentId);
});
it('removes a blob', () => {
return service.create({ uri: contentUri }, {})
.then(blob => blob.id)
.then(id => service.remove(id)).should.eventually.equal(contentId);
});
it('fails to get a non-existent blob', () => {
const response = new MockRes();
return service.get(contentId, { response }).should.be.rejected;
});
afterEach(() => {
// remove the file after each test
// have to check if the file exists first b/c the remove test will (obviously)
// remove the file and `unlink` will fail.
fs.access(join(__dirname, 'blobs', contentId), fs.F_OK, error => {
if (typeof error === 'undefined') {
fs.unlink(join(__dirname, 'blobs', contentId));
}
});
});
after(() => {
// after all tests, remove the blob directory
rimraf(join(__dirname, 'blobs'), {}, error => {
assert.equal(error, null);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment