New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

apx

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apx - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

lib/File.js

0

gruntfile.js

@@ -0,0 +0,0 @@ 'use strict';

'use strict';
module.exports = require('./lib/Apx')

28

lib/Apx.js

@@ -100,2 +100,8 @@ 'use strict';

/**
* File Object
* @type {Function}
*/
Apx.prototype.File = require('./File')
/**
* Initial config schema

@@ -324,3 +330,3 @@ * @type {*}

if('object' === typeof item && !util.isArray(item) && 'string' !== typeof item){
self.sysLog.info('Loading object directly (' + item.name + ')')
self.sysLog.debug('Loading object directly (' + item.name + ')')
iterator(item,finish)

@@ -341,7 +347,7 @@ /**

){
self.sysLog.info('Loading package (' + item + ')')
self.sysLog.debug('Loading package (' + item + ')')
iterator(require(item),finish)
//try to resolve the path with glob
} else {
self.sysLog.info('Trying to glob path (' + item + ')')
self.sysLog.debug('Trying to glob path (' + item + ')')
glob(item,{cwd: cwd},function(err,files){

@@ -352,3 +358,3 @@ if(err) throw err

function(file,next){
self.sysLog.info('Loading package from path (' + self.resolvePath(file) + ')')
self.sysLog.debug('Loading package from path (' + self.resolvePath(file) + ')')
iterator(require(self.resolvePath(file)),next)

@@ -395,3 +401,3 @@ },

if(self.config.exists('middleware')){
self.sysLog.info('Running ' + position + ' middleware for action ' + action.name + ':' + method)
self.sysLog.debug('Running ' + position + ' middleware for action ' + action.name + ':' + method)
self.loadItems(

@@ -420,3 +426,3 @@ self.config.get('middleware'),

var runAction = function(next){
self.sysLog.info('Running action ' + action.name + ' : ' + method)
self.sysLog.debug('Running action ' + action.name + ' : ' + method)
emitter.emit('runActionBefore',action)

@@ -460,3 +466,3 @@ action[method](self,req,res,function(err){

req = new this.Request(req)
this.sysLog.info('Running task ' + task.name)
this.sysLog.debug('Running task ' + task.name)
//run task

@@ -478,4 +484,4 @@ emitter.emit('runTaskBefore',task)

if('string' === typeof service) service = require(this.resolvePath(service))
this.sysLog.info('Returning new service instance for ' + service.name)
return new service.module()
this.sysLog.debug('Returning new service instance for ' + service.name)
return new service.service()
}

@@ -492,5 +498,5 @@

if(!this.services[service.name]){
this.services[service.name] = new service.module()
this.services[service.name] = new service.service()
}
this.sysLog.info('Returning service instance for ' + service.name)
this.sysLog.debug('Returning service instance for ' + service.name)
return this.services[service.name]

@@ -497,0 +503,0 @@ }

'use strict';
var ObjectManage = require('object-manage')
, util = require('util')
, File = require('./File')

@@ -16,2 +17,16 @@ /**

/**
* Reference to an incoming file object should be set by the translator
* @type {Array}
*/
Request.prototype.files = []
/**
* Add file to the request by path
* @param path
*/
Request.prototype.addFile = function(path){
this.files.push(new File(path))
}
module.exports = Request
'use strict';
var ObjectManage = require('object-manage')
, util = require('util')
, SysLog = require('./SysLog')
, File = require('./File')
, mmm = require('mmmagic')
, magic = new mmm.Magic(mmm.MAGIC_MIME_TYPE)
, sysLog = require('./SysLog')

@@ -13,7 +16,6 @@ /**

var Response = function(data){
var self = this
ObjectManage.call(this,data)
self.body = ''
self.rendered = ''
self.warnings = []
this.body = null
this.mimeType = null
this.file = null
}

@@ -23,16 +25,18 @@ util.inherits(Response,ObjectManage)

/**
* String containing raw response data
* Mime type of the body response
* @type {string}
*/
Response.prototype.body = ''
Response.prototype.mimeType = null
/**
* Rendered result of .send()
* String containing raw response data
* @type {string}
*/
Response.prototype.rendered = ''
Response.prototype.body = null
/**
* Warnings from rendering
* @type {Array}
* Reference to an outgoing file object that the translator should consume
* @type {null}
*/
Response.prototype.warnings = []
Response.prototype.file = null

@@ -44,53 +48,119 @@ /**

Response.prototype.add = function(content){
if('object' === typeof content){
this.load(content)
} else if('string' === typeof content){
this.body = this.body + content
if('string' !== typeof content){
throw new Error('Add only works with strings')
}
if('string' !== typeof this.body) this.body = ''
this.body = this.body + content
}
/**
* Send content to client
* @param content Optional (will call .add(content) and then send())
* @returns {*}
* Convenience method to call success
* @param [message] message to send instead of success or an object containing additional params
* @param [code] Code to be sent defaults to 0
* @param [obj] Object to be sent with the response
*/
Response.prototype.send = function(content){
if(undefined === content){
this.rendered = JSON.stringify(this.data)
if('{}' === this.rendered) this.rendered = ''
//warn if sending a combo response
if(this.body !== '' && this.rendered !== '')
this.warn('JSON and String body rendered')
this.rendered = this.rendered + this.body
} else {
this.add(content)
this.send()
Response.prototype.success = function(message,code,obj){
if('number' === typeof message){
code = message
message = undefined
}
if('number' !== typeof code && 'object' === typeof code){
obj = code
code = undefined
}
if('string' !== typeof message && 'object' === typeof message){
obj = message
message = undefined
}
if(!code) code = 0
this.set('status','ok')
this.set('code',code.toString())
this.set('message','success')
if('string' === typeof message)
this.set('message',message)
if('object' === typeof obj)
this.load(obj)
}
/**
* Convenience method to call success
* Convenience method to throw an error
* @param [message]
* @param [code]
* @param [obj]
*/
Response.prototype.success = function(){
this.send({status: 'ok', message: 'success'})
Response.prototype.error = function(message,code,obj){
if('number' === typeof message){
code = message
message = undefined
}
if('number' !== typeof message && 'string' !== typeof message){
obj = message
message = undefined
}
if(!message) message = 'error'
if('string' !== typeof message) message = util.inspect(message)
if(!code) code = 1
if('number' !== typeof code && 'object' === typeof code){
obj = code
code = 1
}
this.set('status','error')
this.set('message',message)
this.set('code',code.toString())
if(obj && 'object' === typeof obj)
this.load(obj)
}
/**
* Convenience method to throw an error
* @param message
* Prepare response to send a file
* @param path Path to the file to be read
* @param [opts] Options to be passed to the file object
*/
Response.prototype.error = function(message){
if('string' !== typeof message) message = util.inspect(message)
this.send({status: 'error', message: util.inspect(message)})
Response.prototype.sendFile = function(path,opts){
this.file = new File(path,opts)
}
/**
* Internal warnings about rendering and parsing
* @param message
* Render response to be prepared for translation
* @param next Callback function
*/
Response.prototype.warn = function(message){
SysLog.warn('Response: ' + message,2)
this.warnings.push(message)
Response.prototype.render = function(next){
var self = this
//check for a file
if(null !== self.file){
self.file.populate(function(){
next(null,{
format: 'file',
file: self.file,
mimeType: self.file.mimeType
})
})
} else if(null === self.mimeType && null !== self.body){
magic.detect(new Buffer(self.body),function(err,mimeType){
if(err){
sysLog.warn('Could not detect mime type of body: ' + err)
mimeType = 'application/octet-stream'
}
self.mimeType = mimeType
next(null,{
format: 'raw',
body: self.body,
mimeType: mimeType
})
})
} else if(null !== self.mimeType && null !== self.body){
next(null,{
format: 'raw',
body: self.body,
mimeType: self.mimeType
})
} else {
next(null,{
format: 'object',
data: self.data,
mimeType: self.mimeType || 'text/json'
})
}
}
module.exports = Response

@@ -0,0 +0,0 @@ 'use strict';

@@ -0,0 +0,0 @@ The MIT License (MIT)

{
"name": "apx",
"version": "0.5.0",
"version": "0.6.0",
"description": "A scalable, extensible, modular API Server",

@@ -41,3 +41,6 @@ "homepage": "https://github.com/snailjs/apx",

"object-manage": "~0.6.0",
"async": "~0.2.9"
"async": "~0.2.9",
"temp": "~0.6.0",
"mime": "~1.2.11",
"mmmagic": "~0.3.5"
},

@@ -44,0 +47,0 @@ "devDependencies": {

@@ -6,3 +6,3 @@ [SnailJS](//github.com/snailjs/).[APX](//github.com/snailjs/apx/)

![Logo](snail-apx.png)
![Logo](http://static.yucache.net/a4e63438eea25b486f1b41979ed12f0ef98b391f?client_file_id=688861)

@@ -167,3 +167,4 @@ ## APX API Server

* **stop** -- Used in initializers and translators to stop instances / listeners, and shutdown cleanly
* **module** -- Used in helpers, models and services to export object to be used
* **pre** -- Used in middleware to be executed before the action
* **post** -- Used in middleware to be executed after the action

@@ -183,3 +184,3 @@ ### Actions

exports.description = 'description of model' //verbose description for generating maps
exports.module = {} //object exported by the helper can also be a constructor
exports.helper = {} //object exported by the helper can also be a constructor
```

@@ -201,3 +202,8 @@

exports.description = 'description of middleware' //verbose description for generating maps
exports.run = function(apx,req,res,next){} //constructor function with a prototype for instantiation
//pre only
exports.run = function(apx,req,res,next){} //constructor function with a prototype for instantiation (for pre run only)
//-- or --
//pre and post
exports.pre = function(apx,req,res,next){} //run this function before the action
exports.post = function(apx,req,res,next){} //runt this function after the action
```

@@ -210,3 +216,4 @@

exports.description = 'description of model' //verbose description for generating maps
exports.module = {} //model object created by desired database software
exports.schema = {} //schema used to create the model
exports.model = {} //model object created by desired database software
```

@@ -219,3 +226,3 @@

exports.description = 'description of service' //verbose description for generating maps
exports.module = function(){} //constructor function with a prototype for instantiation
exports.service = function(){} //constructor function with a prototype for instantiation
```

@@ -553,4 +560,213 @@

## Request and Response Objects
In APX the request and response objects create the common layer that translators iterate. They are instances of
[object-manage](https://github.com/snailjs/object-manage) and expose all the available methods. These objects,
however, **are not streams**. This is a limitation in some cases but it is the only to make the call stack for
middleware usable.
Nearly all API servers implement commands and it is up to the translator to supply any uploaded files which should
be written to temporary files using a tool like [formidable](https://github.com/felixge/node-formidable). After
that the action can direct the file to the correct location or transfer it to a storage cluster.
The request and response objects support a file object that points to an incoming temporary file or a file that should
be streamed to a file.
If there is a situation where file support is more important than command support then raw stream iteration should be
implemented in the translator. It is easy to take existing translators and modify them in userspace.
### Request
The request object contains information that is passed along from the request paths from the translator and is supplied
in object notation. Thus, `req.get('propertyName')` is the most common usage to retrieve information. However to allow
for the receiving of files there is a `req.files` array that shall be populated with files that were written to
temporary files during the request.
In order to access parts of the translator for more control over specific access mediums like the HTTP server the raw
request object shall be provided via `req.raw`
Example
```js
exports.name = 'myAction'
exports.description = 'Example Action'
exports.run = function(apx,req,res,next){
req.get('foo') //bar
req.raw.query.foo //bar
res.success()
next()
}
```
#### Add File
As a convenience for translators to add files to the request object the `addFile()` method exists.
Example
```js
var req = new Request()
req.addFile('foo.txt')
req.addFile('bar.txt')
```
### Response
The response object is built by the action and middleware which is then passed on to the user through the translators
desired output format. In most cases the output format is JSON but this is sometimes configurable at the translator
level.
#### Add Body Content
To allow the sending of raw data rather than JSON or any other format that can be transformed from an object the `add()`
method comes in handy.
Example
```js
exports.name = 'myAction'
exports.description = 'Example Action'
exports.run = function(apx,req,res,next){
res.add('content to show')
res.add('more content to show')
next()
}
```
#### Success
As a convenience method `success()` exists to signal success to the client. This method uses a standard response format.
That resembles the following. Success also accepts a few argument combinations to allow more structured responses
without additional function calls.
Format
```json
{"status": "ok", "message": "success", "code": "0"}
```
Example
```js
exports.name = 'myAction'
exports.description = 'Example Action'
exports.run = function(apx,req,res,next){
res.success() //sends message: 'success', code: '0'
res.success('foo') //sends message: 'foo', code: '0'
res.success('foo',4) //send message: 'foo', code: '4'
res.success('foo',4,{id: 'bar'}) //sends message: 'foo', code: '4', id: 'bar'
res.success('foo',{id: 'bar'}) //sends message: 'foo', code: '0', id: 'bar'
res.success({id: 'bar'}) //sends message: 'success', code: '0', id: 'bar'
next()
}
```
#### Error
For easier error handling the error method can be used to pass an erroneous response to the client and accepts a
few different combinations of arguments to supply the user with information about the error.
Format
```json
{"status": "error", "message": "error", "code": "1"}
```
Example
```js
exports.name = 'myAction'
exports.description = 'Example Action'
exports.run = function(apx,req,res,next){
res.error() //sends message: 'An error has occurred', code: '1'
res.error('foo') //sends message: 'foo', code: '1'
res.error('foo',4) //send message: 'foo', code: '4'
res.error('foo',4,{id: 'bar'}) //sends message: 'foo', code: '4', id: 'bar'
res.error('foo',{id: 'bar'}) //sends message: 'foo', code: '1', id: 'bar'
res.error({id: 'bar'}) //sends message: 'An error has occurred', code: '1', id: 'bar'
next()
}
```
#### Send File
Sometimes it is useful to send clients a file thus the `sendFile(path)` method exists. This will notify the translator
that a file should be sent to the user and allows the translator to properly handle streaming the file to the user.
When a file by that path cannot be found it will throw an exception.
**NOTE** Whenever this method is called it will supersede any other output that has been queued to be sent to the user
by using `add()` or `set()`. Unless the translator is capable of sending multipart responses.
Example
```js
exports.name = 'myAction'
exports.description = 'Example Action'
exports.run = function(apx,req,res,next){
res.sendFile('home.html')
next()
}
```
#### Render
In order for translators to properly handle sending data to their clients there are 3 scenarios which apply to how
the render function will respond.
* **format** this is the format of the response which will be **object**, **raw**, or **file**
* **mimeType** the detected or manually set mime type of the response should be used to respond in various
formats when using the **object** type.
* **body** this is the content container for the **raw** format
* **data** this is the content container for the **object** format and contains the data object to be exported
* **file** this is a reference to the file object when using the **file** format
#### Example
In this example the output will simply be logged to the console.
```js
var xml = require('xml')
, fs = require('fs')
, Response = require('apx/lib/Response')
, res = new Response()
//create our response handler
var responseHandler = function(err,response){
if(err) throw err
if('object' === response.format){
if('text/json' === response.mimeType){
console.log(JSON.stringify(response.data))
} else if('text/xml' === response.mimeType){
console.log(xml(response.data))
} else {
console.warn('desired output type of ' + response.mimeType + ' is not supported defaulting to JSON')
console.log(JSON.stringify(response.data))
}
} else if('raw' === response.format){
console.log(response.body)
} else if('file' === response.format){
var stream = fs.createReadStream(response.file.path)
stream.on('readable',function(){
var chunk
while(null !== (chunk = stream.read())){
console.log(chunk)
}
}
} else {
console.warn('desired output format of ' + response.format + ' is not supported, not sending output')
}
}
//implement the handler
res.render(responseHandler)
```
## Changelog
### 0.6.0
* Request objects now implement the file object
* Response objects now implement the file object
* Response.send() removed in favor of the existing **object-manage** functions
* In order to support `pre` and `post` middleware translators need to fire the `Response.render()` method and then send
the rendered response to their client
* Upgraded **middleware**, **model**, **service** and **helper** plugin format. This will require upgrading of existing
plugins and makes this version incompatible with older plugins.
* Added `Response.sendFile(path)` to allow sending of files to the user. Using this method still requires calling `next()`
and will supersede any existing output.
* Improved `Response.success()` and `Response.error()` to accept a better combination of arguments to produce
usable responses with a single function call.
### 0.5.0

@@ -557,0 +773,0 @@

@@ -104,3 +104,3 @@ 'use strict';

var service = {
module: function(){
service: function(){
this.mystuff = 'val1'

@@ -119,3 +119,3 @@ }

var service = {
module: function(){
service: function(){
this.mystuff = 'val1'

@@ -122,0 +122,0 @@ }

'use strict';
var expect = require('chai').expect
, temp = require('temp')
, fs = require('fs')
, Request = require('../lib/Request')

@@ -35,3 +37,3 @@ describe('APX Request',function(){

})
it('should be ale to remove a key and children',function(){
it('should be able to remove a key and children',function(){
var req = new Request([data1,data3])

@@ -41,2 +43,14 @@ req.remove('test5')

})
it('should allow adding of files from translator',function(){
var tmpFile1 = temp.openSync()
, tmpFile2 = temp.openSync()
fs.writeSync(tmpFile1.fd,'foo')
fs.writeSync(tmpFile2.fd,'bar')
var req = new Request()
req.addFile(tmpFile1.path)
req.addFile(tmpFile2.path)
expect(req.files[0].path).to.equal(tmpFile1.path)
expect(req.files[1].path).to.equal(tmpFile2.path)
temp.cleanup()
})
})
'use strict';
var expect = require('chai').expect
, Response = require('../lib/Response')
, temp = require('temp')
, fs = require('fs')
describe('APX Response',function(){

@@ -14,32 +16,152 @@ describe('methods',function(){

})
it('should accept an object to add to the body',function(){
res.add({mydata: 'val1'})
it('should accept an object to add to the object',function(){
res.load({mydata: 'val1'})
expect(res.get('mydata')).to.equal('val1')
})
it('should accept an array of objects to add to the body',function(){
res.add([{mydata: 'val1'},{mydata2: 'val2'}])
res.load([{mydata: 'val1'},{mydata2: 'val2'}])
expect(res.get('mydata')).to.equal('val1')
expect(res.get('mydata2')).to.equal('val2')
})
it('should send and fire the callback',function(){
res.send('foo')
expect(res.rendered).to.equal('foo')
})
it('should send JSON of the data object',function(){
res.send({mydata: 'val1'})
var obj = JSON.parse(res.rendered)
res.load({mydata: 'val1'})
var obj = JSON.parse(JSON.stringify(res.get()))
expect(obj.mydata).to.equal('val1')
})
it('should send body for a string response',function(){
res.send('foo')
expect(res.rendered).to.equal('foo')
it('should support sending of a file',function(){
//create a temp file
var tmpFile = temp.openSync()
fs.writeSync(tmpFile.fd,'foo')
res.sendFile(tmpFile.path)
expect(res.file.path).to.equal(tmpFile.path)
temp.cleanup()
})
it('should send JSON + body if both were set by throw a warning',function(){
res.add({mydata: 'val1'})
res.send('foo')
expect(res.warnings[0]).to.equal('JSON and String body rendered')
expect(res.rendered).to.equal('{"mydata":"val1"}foo')
it('should allow sending of success',function(){
res.success()
expect(res.get('status')).to.equal('ok')
expect(res.get('message')).to.equal('success')
})
it('should allow sending of success with an object',function(){
res.success({id: 'foo'})
expect(res.get('status')).to.equal('ok')
expect(res.get('code')).to.equal('0')
expect(res.get('message')).to.equal('success')
expect(res.get('id')).to.equal('foo')
})
it('should allow sending of success with a message',function(){
res.success('foo')
expect(res.get('status')).to.equal('ok')
expect(res.get('code')).to.equal('0')
expect(res.get('message')).to.equal('foo')
})
it('should allow sending of success with a message and code',function(){
res.success('foo',2)
expect(res.get('status')).to.equal('ok')
expect(res.get('code')).to.equal('2')
expect(res.get('message')).to.equal('foo')
})
it('should allow sending of success with a message, code, and object',function(){
res.success('foo',2,{id: 'foo'})
expect(res.get('status')).to.equal('ok')
expect(res.get('code')).to.equal('2')
expect(res.get('message')).to.equal('foo')
expect(res.get('id')).to.equal('foo')
})
it('should allow sending of success with a message and object',function(){
res.success('foo',{id: 'foo'})
expect(res.get('status')).to.equal('ok')
expect(res.get('code')).to.equal('0')
expect(res.get('message')).to.equal('foo')
expect(res.get('id')).to.equal('foo')
})
it('should allow sending of errors',function(){
res.error()
expect(res.get('status')).to.equal('error')
expect(res.get('message')).to.equal('error')
expect(res.get('code')).to.equal('1')
})
it('should allow sending of errors with a customer message',function(){
res.error('foo')
expect(res.get('status')).to.equal('error')
expect(res.get('message')).to.equal('foo')
expect(res.get('code')).to.equal('1')
})
it('should allow sending of errors with only a code',function(){
res.error(4)
expect(res.get('status')).to.equal('error')
expect(res.get('message')).to.equal('error')
expect(res.get('code')).to.equal('4')
})
it('should allow sending of errors with only an object',function(){
res.error({foo: 'bar'})
expect(res.get('status')).to.equal('error')
expect(res.get('message')).to.equal('error')
expect(res.get('code')).to.equal('1')
expect(res.get('foo')).to.equal('bar')
})
it('should allow sending of errors with a messages, code and object',function(){
res.error('baz',2,{foo: 'bar'})
expect(res.get('status')).to.equal('error')
expect(res.get('message')).to.equal('baz')
expect(res.get('code')).to.equal('2')
expect(res.get('foo')).to.equal('bar')
})
it('should allow sending of errors with a message and object',function(){
res.error('baz',{foo: 'bar'})
expect(res.get('status')).to.equal('error')
expect(res.get('message')).to.equal('baz')
expect(res.get('code')).to.equal('1')
expect(res.get('foo')).to.equal('bar')
})
it('should render with an object intended for json',function(done){
res.success('foo')
res.render(function(err,response){
expect(response.format).to.equal('object')
expect(response.data.status).to.equal('ok')
expect(response.mimeType).to.equal('text/json')
done()
})
})
it('should render with an object intended for xml by setting the mime type manually',function(done){
res.success('foo')
res.mimeType = 'text/xml'
res.render(function(err,response){
expect(response.format).to.equal('object')
expect(response.data.status).to.equal('ok')
expect(response.mimeType).to.equal('text/xml')
done()
})
})
it('should render a raw response and auto detect the mime type',function(done){
res.add('foo')
res.render(function(err,response){
expect(response.format).to.equal('raw')
expect(response.body).to.equal('foo')
expect(response.mimeType).to.equal('text/plain')
done()
})
})
it('should render a raw response with a manually set mime type',function(done){
res.add('{"foo": "bar"}')
res.mimeType = 'text/json'
res.render(function(err,response){
expect(response.format).to.equal('raw')
expect(response.body).to.equal('{"foo": "bar"}')
expect(response.mimeType).to.equal('text/json')
done()
})
})
it('should render a file response and auto detect the mime type',function(done){
var tmpFile = temp.openSync()
fs.writeSync(tmpFile.fd,'foo')
res.sendFile(tmpFile.path)
res.render(function(err,response){
expect(response.format).to.equal('file')
expect(response.file).to.be.an('object')
expect(response.mimeType).to.equal('text/plain')
temp.cleanup()
done()
})
})
})
})

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc