Added few more operation support in the API
This commit is contained in:
parent
078f6c518c
commit
1abe64cb09
6 changed files with 238 additions and 170 deletions
|
@ -114,6 +114,14 @@ export default class MediaRoutes {
|
||||||
|
|
||||||
let keys = Object.keys(ctx.req.body)
|
let keys = Object.keys(ctx.req.body)
|
||||||
|
|
||||||
|
let allowedOperations = [
|
||||||
|
'trim',
|
||||||
|
'flatten',
|
||||||
|
'resize',
|
||||||
|
'blur',
|
||||||
|
'extend',
|
||||||
|
]
|
||||||
|
|
||||||
await Promise.all(keys.map(key => {
|
await Promise.all(keys.map(key => {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
|
@ -121,8 +129,10 @@ export default class MediaRoutes {
|
||||||
let sharp = this.sharp(`./public/${ctx.state.site}/${ctx.body.filename}`)
|
let sharp = this.sharp(`./public/${ctx.state.site}/${ctx.body.filename}`)
|
||||||
.rotate()
|
.rotate()
|
||||||
|
|
||||||
if (item.resize) {
|
for (let operation of allowedOperations) {
|
||||||
sharp = sharp.resize(item.resize)
|
if (item[operation] != null) {
|
||||||
|
sharp = sharp[operation](item[operation])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sharp = sharp[item.format](item[item.format])
|
sharp = sharp[item.format](item[item.format])
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,17 @@ export function throwIfNotPublic(site) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validObjectOperations = [
|
||||||
|
'resize',
|
||||||
|
'extend',
|
||||||
|
'flatten',
|
||||||
|
]
|
||||||
|
|
||||||
|
const validNumberOperations = [
|
||||||
|
'blur',
|
||||||
|
'trim',
|
||||||
|
]
|
||||||
|
|
||||||
export function verifyBody(ctx) {
|
export function verifyBody(ctx) {
|
||||||
let keys = Object.keys(ctx.req.body)
|
let keys = Object.keys(ctx.req.body)
|
||||||
|
|
||||||
|
@ -49,7 +60,7 @@ export function verifyBody(ctx) {
|
||||||
|
|
||||||
if (typeof(item.format) !== 'string'
|
if (typeof(item.format) !== 'string'
|
||||||
|| !item.format
|
|| !item.format
|
||||||
|| item.format === 'resize'
|
|| validObjectOperations.includes(item.format)
|
||||||
|| item.format === 'out') {
|
|| item.format === 'out') {
|
||||||
throw new HttpError(`Body item ${key} missing valid format`, 422)
|
throw new HttpError(`Body item ${key} missing valid format`, 422)
|
||||||
}
|
}
|
||||||
|
@ -68,10 +79,20 @@ export function verifyBody(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.resize != null) {
|
for (let operation of validObjectOperations) {
|
||||||
if (typeof(item.resize) !== 'object'
|
if (item[operation] != null) {
|
||||||
|| Array.isArray(item.resize)) {
|
if (typeof(item[operation]) !== 'object'
|
||||||
throw new HttpError(`Body item ${key} key resize was invalid`, 422)
|
|| Array.isArray(item[operation])) {
|
||||||
|
throw new HttpError(`Body item ${key} key ${operation} was invalid`, 422)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let operation of validNumberOperations) {
|
||||||
|
if (item[operation] != null) {
|
||||||
|
if (typeof(item[operation]) !== 'number') {
|
||||||
|
throw new HttpError(`Body item ${key} key ${operation} was invalid`, 422)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,62 +113,3 @@ Client.prototype.upload = function(url, file, method = 'POST', body = {}) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
export function createClient(host = config.get('server:port'), opts) {
|
|
||||||
let options = defaults(opts, {})
|
|
||||||
|
|
||||||
let prefix = `http://localhost:${host}`
|
|
||||||
options.headers['x-request-id'] = 'asdf'
|
|
||||||
|
|
||||||
client.auth = (user) => {
|
|
||||||
// let m = helperDB.model('user', {
|
|
||||||
// id: user.id,
|
|
||||||
// level: (user.get && user.get('level')) || 1,
|
|
||||||
// institute_id: (user.get && user.get('institute_id')) || null,
|
|
||||||
// password: (user.get && user.get('password')) || null,
|
|
||||||
// })
|
|
||||||
// let token = jwt.createUserToken(m)
|
|
||||||
// client.headers.authorization = `Bearer ${token}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple wrappers to wrap into promises
|
|
||||||
client.getAsync = (path) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
if (path.slice(0, 4) === 'http') {
|
|
||||||
return client.get(path, callback(resolve, reject))
|
|
||||||
}
|
|
||||||
client.get(prefix + path, callback(resolve, reject))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Simple wrappers to wrap into promises
|
|
||||||
client.saveFileAsync = (path, destination) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
client.saveFile(prefix + path, destination, callback(resolve, reject, true))
|
|
||||||
})
|
|
||||||
|
|
||||||
client.postAsync = (path, data) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
client.post(prefix + path, data, callback(resolve, reject))
|
|
||||||
})
|
|
||||||
|
|
||||||
client.putAsync = (path, data) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
client.put(prefix + path, data, callback(resolve, reject))
|
|
||||||
})
|
|
||||||
|
|
||||||
client.deleteAsync = (path) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
client.del(prefix + path, callback(resolve, reject))
|
|
||||||
})
|
|
||||||
|
|
||||||
client.sendFileAsync = (path, files, data) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
client.sendFile(prefix + path, files, data || {}, callback(resolve, reject))
|
|
||||||
})
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
|
@ -278,6 +278,10 @@ t.describe('Media (API)', () => {
|
||||||
resize: {
|
resize: {
|
||||||
width: 300,
|
width: 300,
|
||||||
},
|
},
|
||||||
|
blur: 0.5,
|
||||||
|
flatten: {r:0,g:0,b:0},
|
||||||
|
trim: 1,
|
||||||
|
extend: { top: 10, left: 10, bottom: 10, right: 10, background: {r:0,g:0,b:0} },
|
||||||
jpeg: {
|
jpeg: {
|
||||||
quality: 80,
|
quality: 80,
|
||||||
mozjpeg: true,
|
mozjpeg: true,
|
||||||
|
@ -324,8 +328,8 @@ t.describe('Media (API)', () => {
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
|
|
||||||
img = await sharp(resolve(`../../public/${data.test1.path}`)).metadata()
|
img = await sharp(resolve(`../../public/${data.test1.path}`)).metadata()
|
||||||
assert.strictEqual(img.width, 300)
|
assert.strictEqual(img.width, 320)
|
||||||
assert.strictEqual(img.height, 350)
|
assert.strictEqual(img.height, 413)
|
||||||
assert.strictEqual(img.format, 'jpeg')
|
assert.strictEqual(img.format, 'jpeg')
|
||||||
|
|
||||||
img = await sharp(resolve(`../../public/${data.test2.path}`)).metadata()
|
img = await sharp(resolve(`../../public/${data.test2.path}`)).metadata()
|
||||||
|
|
|
@ -285,6 +285,10 @@ t.describe('#resize', function() {
|
||||||
const stubUpload = stub()
|
const stubUpload = stub()
|
||||||
const stubSharp = stub()
|
const stubSharp = stub()
|
||||||
const stubSharpResize = stub()
|
const stubSharpResize = stub()
|
||||||
|
const stubSharpBlur = stub()
|
||||||
|
const stubSharpTrim = stub()
|
||||||
|
const stubSharpExtend = stub()
|
||||||
|
const stubSharpFlatten = stub()
|
||||||
const stubSharpRotate = stub()
|
const stubSharpRotate = stub()
|
||||||
const stubSharpToFile = stub()
|
const stubSharpToFile = stub()
|
||||||
const stubSharpJpeg = stub()
|
const stubSharpJpeg = stub()
|
||||||
|
@ -311,6 +315,10 @@ t.describe('#resize', function() {
|
||||||
jpeg: stubSharpJpeg,
|
jpeg: stubSharpJpeg,
|
||||||
png: stubSharpPng,
|
png: stubSharpPng,
|
||||||
resize: stubSharpResize,
|
resize: stubSharpResize,
|
||||||
|
trim: stubSharpTrim,
|
||||||
|
extend: stubSharpExtend,
|
||||||
|
flatten: stubSharpFlatten,
|
||||||
|
blur: stubSharpBlur,
|
||||||
rotate: stubSharpRotate,
|
rotate: stubSharpRotate,
|
||||||
toBuffer: stubSharpToBuffer,
|
toBuffer: stubSharpToBuffer,
|
||||||
}
|
}
|
||||||
|
@ -318,17 +326,11 @@ t.describe('#resize', function() {
|
||||||
stubStat.resolves({ size: 0 })
|
stubStat.resolves({ size: 0 })
|
||||||
stubSharp.reset()
|
stubSharp.reset()
|
||||||
stubSharp.returns(def)
|
stubSharp.returns(def)
|
||||||
stubSharpToFile.reset()
|
for (let key in def) {
|
||||||
|
def[key].reset()
|
||||||
|
def[key].returns(def)
|
||||||
|
}
|
||||||
stubSharpToFile.resolves()
|
stubSharpToFile.resolves()
|
||||||
stubSharpJpeg.reset()
|
|
||||||
stubSharpJpeg.returns(def)
|
|
||||||
stubSharpResize.reset()
|
|
||||||
stubSharpResize.returns(def)
|
|
||||||
stubSharpRotate.reset()
|
|
||||||
stubSharpRotate.returns(def)
|
|
||||||
stubSharpPng.reset()
|
|
||||||
stubSharpPng.returns(def)
|
|
||||||
stubSharpToBuffer.reset()
|
|
||||||
stubSharpToBuffer.resolves()
|
stubSharpToBuffer.resolves()
|
||||||
routes.siteCache.clear()
|
routes.siteCache.clear()
|
||||||
}
|
}
|
||||||
|
@ -381,11 +383,28 @@ t.describe('#resize', function() {
|
||||||
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should call sharp correctly if items and resize are specified', async function() {
|
let validOperations = [
|
||||||
|
'resize',
|
||||||
|
'blur',
|
||||||
|
'trim',
|
||||||
|
'extend',
|
||||||
|
'flatten',
|
||||||
|
]
|
||||||
|
|
||||||
|
let operationStubMap = {
|
||||||
|
resize: stubSharpResize,
|
||||||
|
blur: stubSharpBlur,
|
||||||
|
trim: stubSharpTrim,
|
||||||
|
extend: stubSharpExtend,
|
||||||
|
flatten: stubSharpFlatten,
|
||||||
|
}
|
||||||
|
|
||||||
|
validOperations.forEach(function(operation) {
|
||||||
|
t.test(`should call sharp correctly if items and ${operation} are specified`, async function() {
|
||||||
reset()
|
reset()
|
||||||
const assertKey = 'herpderp'
|
const assertKey = 'herpderp'
|
||||||
const assertPng = { a: 1 }
|
const assertPng = { a: 1 }
|
||||||
const assertResize = { a: 1 }
|
const assertPayload = { a: 1 }
|
||||||
const assertFilename = 'asdfsafd'
|
const assertFilename = 'asdfsafd'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.resolves(assertSite)
|
||||||
|
@ -403,7 +422,7 @@ t.describe('#resize', function() {
|
||||||
}
|
}
|
||||||
}}})
|
}}})
|
||||||
ctx.req.body[assertKey].png = assertPng
|
ctx.req.body[assertKey].png = assertPng
|
||||||
ctx.req.body[assertKey].resize = assertResize
|
ctx.req.body[assertKey][operation] = assertPayload
|
||||||
|
|
||||||
await routes.resize(ctx)
|
await routes.resize(ctx)
|
||||||
|
|
||||||
|
@ -415,8 +434,10 @@ t.describe('#resize', function() {
|
||||||
assert.ok(stubSharpToFile.called)
|
assert.ok(stubSharpToFile.called)
|
||||||
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.png`))
|
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.png`))
|
||||||
assert.notOk(stubSharpJpeg.called)
|
assert.notOk(stubSharpJpeg.called)
|
||||||
assert.ok(stubSharpResize.called)
|
|
||||||
assert.strictEqual(stubSharpResize.firstCall[0], assertResize)
|
assert.ok(operationStubMap[operation].called)
|
||||||
|
assert.strictEqual(operationStubMap[operation].firstCall[0], assertPayload)
|
||||||
|
|
||||||
assert.strictEqual(ctx.body.filename, assertFilename + '.png')
|
assert.strictEqual(ctx.body.filename, assertFilename + '.png')
|
||||||
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}.png`)
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}.png`)
|
||||||
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.png`)
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.png`)
|
||||||
|
@ -429,6 +450,7 @@ t.describe('#resize', function() {
|
||||||
assert.strictEqual(filesFromCache[1].filename, assertFilename + '.png')
|
assert.strictEqual(filesFromCache[1].filename, assertFilename + '.png')
|
||||||
assert.strictEqual(filesFromCache[1].size, 10)
|
assert.strictEqual(filesFromCache[1].size, 10)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.test('should notify which item failed if one fails', async function() {
|
t.test('should notify which item failed if one fails', async function() {
|
||||||
reset()
|
reset()
|
||||||
|
|
|
@ -323,7 +323,14 @@ t.describe('#verifyBody()', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should allow empty value or object in resize', function() {
|
let validObjectOperations = [
|
||||||
|
'resize',
|
||||||
|
'extend',
|
||||||
|
'flatten',
|
||||||
|
]
|
||||||
|
|
||||||
|
validObjectOperations.forEach(function(operation) {
|
||||||
|
t.test(`should allow empty value or object in ${operation}`, function() {
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
item: {
|
item: {
|
||||||
format: 'test',
|
format: 'test',
|
||||||
|
@ -338,15 +345,15 @@ t.describe('#verifyBody()', function() {
|
||||||
]
|
]
|
||||||
|
|
||||||
tests.forEach(function (check) {
|
tests.forEach(function (check) {
|
||||||
ctx.req.body.item.resize = check[0]
|
ctx.req.body.item[operation] = check[0]
|
||||||
|
|
||||||
assert.doesNotThrow(function() {
|
assert.doesNotThrow(function() {
|
||||||
verifyBody(ctx)
|
verifyBody(ctx)
|
||||||
}, `should not throw with ${check[1]} in resize`)
|
}, `should not throw with ${check[1]} in ${operation}`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should fail if resize if specified is invalid', function() {
|
t.test(`should fail if ${operation} if specified is invalid`, function() {
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
item: {
|
item: {
|
||||||
format: 'test',
|
format: 'test',
|
||||||
|
@ -363,7 +370,7 @@ t.describe('#verifyBody()', function() {
|
||||||
]
|
]
|
||||||
|
|
||||||
tests.forEach(function (check) {
|
tests.forEach(function (check) {
|
||||||
ctx.req.body.item.resize = check[0]
|
ctx.req.body.item[operation] = check[0]
|
||||||
|
|
||||||
assert.throws(function() { verifyBody(ctx) }, function(err) {
|
assert.throws(function() { verifyBody(ctx) }, function(err) {
|
||||||
assert.ok(err instanceof HttpError)
|
assert.ok(err instanceof HttpError)
|
||||||
|
@ -371,10 +378,73 @@ t.describe('#verifyBody()', function() {
|
||||||
assert.strictEqual(err.status, 422)
|
assert.strictEqual(err.status, 422)
|
||||||
assert.match(err.message, /body/i)
|
assert.match(err.message, /body/i)
|
||||||
assert.match(err.message, /item/i)
|
assert.match(err.message, /item/i)
|
||||||
assert.match(err.message, /resize/i)
|
assert.match(err.message, new RegExp(operation))
|
||||||
assert.match(err.message, /valid/i)
|
assert.match(err.message, /valid/i)
|
||||||
return true
|
return true
|
||||||
}, `should fail if body item resize is ${check[1]}`)
|
}, `should fail if body item ${operation} is ${check[1]}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let validNumberOperations = [
|
||||||
|
'blur',
|
||||||
|
'trim',
|
||||||
|
]
|
||||||
|
|
||||||
|
validNumberOperations.forEach(function(operation) {
|
||||||
|
t.test(`should allow empty value or number in ${operation}`, function() {
|
||||||
|
let ctx = createContext({ req: { body: {
|
||||||
|
item: {
|
||||||
|
format: 'test',
|
||||||
|
test: {},
|
||||||
|
}
|
||||||
|
} } })
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
[undefined, 'undefined'],
|
||||||
|
[null, 'null'],
|
||||||
|
[0, 'number'],
|
||||||
|
[0.5, 'positive number'],
|
||||||
|
]
|
||||||
|
|
||||||
|
tests.forEach(function (check) {
|
||||||
|
ctx.req.body.item[operation] = check[0]
|
||||||
|
|
||||||
|
assert.doesNotThrow(function() {
|
||||||
|
verifyBody(ctx)
|
||||||
|
}, `should not throw with ${check[1]} in ${operation}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test(`should fail if ${operation} if specified is invalid`, function() {
|
||||||
|
let ctx = createContext({ req: { body: {
|
||||||
|
item: {
|
||||||
|
format: 'test',
|
||||||
|
test: {},
|
||||||
|
}
|
||||||
|
} } })
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
['', 'emptystring'],
|
||||||
|
['asdf', 'string'],
|
||||||
|
[{}, 'object'],
|
||||||
|
[[], 'array'],
|
||||||
|
]
|
||||||
|
|
||||||
|
tests.forEach(function (check) {
|
||||||
|
ctx.req.body.item[operation] = check[0]
|
||||||
|
|
||||||
|
assert.throws(function() { verifyBody(ctx) }, function(err) {
|
||||||
|
assert.ok(err instanceof HttpError)
|
||||||
|
assert.ok(err instanceof Error)
|
||||||
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, /body/i)
|
||||||
|
assert.match(err.message, /item/i)
|
||||||
|
assert.match(err.message, new RegExp(operation))
|
||||||
|
assert.match(err.message, /valid/i)
|
||||||
|
return true
|
||||||
|
}, `should fail if body item ${operation} is ${check[1]}`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue