Last active
August 11, 2016 11:39
-
-
Save thebarndog/4f253baecfdc1f42eb30acd3bb1ac9c7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
}); | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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