fortune-http
Version:
HTTP implementation for Fortune.js.
199 lines (154 loc) • 5.67 kB
JavaScript
var buffer = Buffer.from || Buffer
module.exports = function (HttpSerializer) {
var common, assign, map, message, methods, errors, keys, castValue
/**
* This is an ad hoc JSON-over-HTTP serializer, which is suitable only for
* prototyping or internal use.
*/
function JsonSerializer (properties) {
HttpSerializer.call(this, properties)
common = this.common
assign = common.assign
map = common.map
message = common.message
methods = common.methods
errors = common.errors
keys = common.keys
castValue = common.castValue
}
JsonSerializer.prototype = Object.create(HttpSerializer.prototype)
JsonSerializer.prototype.processResponse =
function (contextResponse, request, response) {
var jsonSpaces = this.options.jsonSpaces || 2
var bufferEncoding = this.options.bufferEncoding || 'base64'
var payload = contextResponse.payload
var meta = contextResponse.meta || {}
var method = request.meta.method
var updateModified = meta.updateModified
var recordTypes = this.recordTypes
var documentation = this.documentation
var output
// Delete and update requests may not respond with anything.
if (method === methods.delete ||
(method === methods.update && !updateModified)) {
delete contextResponse.payload
return contextResponse
}
// Set the charset to UTF-8.
response.setHeader('Content-Type',
JsonSerializer.mediaType + '; charset=utf-8')
if (payload != null)
contextResponse.payload = JSON.stringify(payload,
function (key, value) {
// Duck type checking for buffer stringification.
if (value && value.type === 'Buffer' &&
Array.isArray(value.data) &&
Object.keys(value).length === 2)
return buffer(value.data).toString(bufferEncoding)
return value
}, jsonSpaces)
else if (contextResponse instanceof Error) {
// Skip setting payload if method is invalid.
if (contextResponse.isMethodInvalid)
return contextResponse
// If the error is type unspecified, show the index.
if (contextResponse.isTypeUnspecified) {
output = {
recordTypes: recordTypes,
documentation: documentation
}
response.statusCode = 200
}
else output = {
name: contextResponse.name,
message: contextResponse.message
}
contextResponse.payload = JSON.stringify(output,
function (key, value) {
if (typeof value === 'function') return value.name
return value
}, jsonSpaces)
}
return contextResponse
}
JsonSerializer.prototype.parsePayload = function (contextRequest) {
var method = contextRequest.method
var language = contextRequest.meta.language
var MethodError = errors.MethodError
if (method === methods.create) return this.parseCreate(contextRequest)
else if (method === methods.update) return this.parseUpdate(contextRequest)
throw new MethodError(message(
'InvalidMethod', language, { method: method }))
}
JsonSerializer.prototype.parseCreate = function (contextRequest) {
var opts = { language: contextRequest.meta.language }
var options = this.options
var recordTypes = this.recordTypes
var typeKey = keys.type
var type = contextRequest.type
var fields = recordTypes[type]
var i, j, records, record, field, value, fieldDefinition, fieldType
records = parseBuffer.call(this, contextRequest.payload)
if (!Array.isArray(records)) records = [ records ]
for (i = 0, j = records.length; i < j; i++) {
record = records[i]
for (field in record) {
value = record[field]
fieldDefinition = fields[field] || {}
fieldType = fieldDefinition[typeKey]
record[field] = Array.isArray(value) ?
map(value, curryCast(castValue, fieldType, assign(opts, options))) :
castValue(value, fieldType, assign(opts, options))
}
}
return records
}
JsonSerializer.prototype.parseUpdate = function (contextRequest) {
var update, updates
var i, j
updates = parseBuffer.call(this, contextRequest.payload)
if (!Array.isArray(updates)) updates = [ updates ]
for (i = 0, j = updates.length; i < j; i++) {
update = updates[i]
castFields.call(this, contextRequest,
update.replace, update.push, update.pull)
}
return updates
}
JsonSerializer.mediaType = 'application/json'
return JsonSerializer
function parseBuffer (payload) {
var BadRequestError = errors.BadRequestError
if (!Buffer.isBuffer(payload)) return null
try {
return JSON.parse(payload.toString())
}
catch (error) {
throw new BadRequestError(error.message)
}
}
function castFields (contextRequest) {
var opts = { language: contextRequest.meta.language }
var options = this.options
var typeKey = keys.type
var fields = this.recordTypes[contextRequest.type]
var i, j, object, field, value, fieldDefinition, fieldType
for (i = 1, j = arguments.length; i < j; i++) {
object = arguments[i]
for (field in object) {
value = object[field]
fieldDefinition = fields[field] || {}
fieldType = fieldDefinition[typeKey]
object[field] = Array.isArray(value) ?
map(value, curryCast(castValue, fieldType, assign(opts, options))) :
castValue(value, fieldType, assign(opts, options))
}
}
}
}
function curryCast (fn, type, options) {
return function (value) {
return fn(value, type, options)
}
}