mirror of
https://github.com/wekan/wekan.git
synced 2025-12-20 01:10:12 +01:00
Fixed Non-ASCII attachment filename will crash when downloading.
Thanks to xet7 ! Fixes #2759
This commit is contained in:
parent
843ff8eaaa
commit
c2da477735
277 changed files with 30568 additions and 52 deletions
18
packages/wekan-cfs-http-publish/.editorconfig
Normal file
18
packages/wekan-cfs-http-publish/.editorconfig
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# .editorconfig
|
||||
# Meteor adapted EditorConfig, http://EditorConfig.org
|
||||
# By RaiX 2013
|
||||
|
||||
root = true
|
||||
|
||||
[*.js]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
max_line_length = 80
|
||||
indent_brace_style = 1TBS
|
||||
spaces_around_operators = true
|
||||
quote_type = auto
|
||||
# curly_bracket_next_line = true
|
||||
4
packages/wekan-cfs-http-publish/.gitignore
vendored
Normal file
4
packages/wekan-cfs-http-publish/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/versions.json
|
||||
.build*
|
||||
smart.lock
|
||||
/nbproject/
|
||||
97
packages/wekan-cfs-http-publish/.jshintrc
Normal file
97
packages/wekan-cfs-http-publish/.jshintrc
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
// JSHint Meteor Configuration File
|
||||
// Match the Meteor Style Guide
|
||||
//
|
||||
// By @raix with contributions from @aldeed and @awatson1978
|
||||
// Source https://github.com/raix/Meteor-jshintrc
|
||||
//
|
||||
// See http://jshint.com/docs/ for more details
|
||||
|
||||
"maxerr": 50,
|
||||
"bitwise": true,
|
||||
"camelcase": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"forin": true,
|
||||
"immed": false,
|
||||
"indent": 2,
|
||||
"latedef": false,
|
||||
"newcap": false,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"nonew": false,
|
||||
"plusplus": false,
|
||||
"quotmark": false,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"maxparams": false,
|
||||
"maxdepth": false,
|
||||
"maxstatements": false,
|
||||
"maxcomplexity": false,
|
||||
"maxlen": 80,
|
||||
"asi": false,
|
||||
"boss": false,
|
||||
"debug": false,
|
||||
"eqnull": false,
|
||||
"es5": false,
|
||||
"esnext": false,
|
||||
"moz": false,
|
||||
"evil": false,
|
||||
"expr": false,
|
||||
"funcscope": false,
|
||||
"globalstrict": true,
|
||||
"iterator": false,
|
||||
"lastsemic": false,
|
||||
"laxbreak": false,
|
||||
"laxcomma": false,
|
||||
"loopfunc": false,
|
||||
"multistr": false,
|
||||
"proto": false,
|
||||
"scripturl": false,
|
||||
"smarttabs": false,
|
||||
"shadow": false,
|
||||
"sub": false,
|
||||
"supernew": false,
|
||||
"validthis": false,
|
||||
"browser": true,
|
||||
"couch": false,
|
||||
"devel": true,
|
||||
"dojo": false,
|
||||
"jquery": false,
|
||||
"mootools": false,
|
||||
"node": false,
|
||||
"nonstandard": false,
|
||||
"prototypejs": false,
|
||||
"rhino": false,
|
||||
"worker": false,
|
||||
"wsh": false,
|
||||
"yui": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"passfail": false,
|
||||
"white": false,
|
||||
"predef": [
|
||||
"Meteor",
|
||||
"Accounts",
|
||||
"Session",
|
||||
"Template",
|
||||
"check",
|
||||
"Match",
|
||||
"Deps",
|
||||
"EJSON",
|
||||
"Email",
|
||||
"Package",
|
||||
"Tinytest",
|
||||
"Npm",
|
||||
"Assets",
|
||||
"Packages",
|
||||
"process",
|
||||
"LocalCollection",
|
||||
"_",
|
||||
"Random",
|
||||
"HTTP",
|
||||
"_methodHTTP"
|
||||
]
|
||||
}
|
||||
5
packages/wekan-cfs-http-publish/.travis.yml
Normal file
5
packages/wekan-cfs-http-publish/.travis.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
before_install:
|
||||
- "curl -L http://git.io/s0Zu-w | /bin/sh"
|
||||
20
packages/wekan-cfs-http-publish/LICENSE.md
Normal file
20
packages/wekan-cfs-http-publish/LICENSE.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 [@raix](https://github.com/raix), aka Morten N.O. Nørgaard Henriksen, mh@gi-software.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
129
packages/wekan-cfs-http-publish/README.md
Normal file
129
packages/wekan-cfs-http-publish/README.md
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
wekan-cfs-http-publish [](https://travis-ci.org/CollectionFS/Meteor-http-publish)
|
||||
============
|
||||
|
||||
This package add the ability to add `HTTP` server publish to your project. It's a server-side package only.
|
||||
|
||||
DEPRECATING: Use https://atmospherejs.com/simple/rest instead
|
||||
|
||||
## Usage
|
||||
HTTP.publish creates a http crud restpoint for a collection *- only one cursor is allowed pr. publish*
|
||||
|
||||
### Security
|
||||
`CRUD+L` - Create Read Update Delete + List are common rest point operations.
|
||||
|
||||
All `CUD` methods are the exact same as the `ddp` methods handlers - This means that `Meteor.allow` and `Meteor.deny` are setting the access rules for both `ddp` and `http` collection methods.
|
||||
|
||||
All `R+L` methods are limited to the publish function.
|
||||
|
||||
### Fully mounted
|
||||
If handed a collection and a publish function the HTTP.publish will mount on follow urls and methods:
|
||||
* `GET` - `/api/list` *- all published data*
|
||||
* `POST` - `/api/list` *- insert a document into collection*
|
||||
* `GET` - `/api/list/:id` *- find one published document*
|
||||
* `PUT` - `/api/list/:id` *- update a document*
|
||||
* `DELETE` - `/api/list/:id` *- remove a document*
|
||||
|
||||
```js
|
||||
myCollection = new Meteor.Collection('list');
|
||||
|
||||
// Add access points for `GET`, `POST`, `PUT`, `DELETE`
|
||||
HTTP.publish({collection: myCollection}, function(data) {
|
||||
// this.userId, this.query, this.params
|
||||
return myCollection.find({});
|
||||
});
|
||||
```
|
||||
|
||||
### Publish view only
|
||||
If handed a mount name and a publish function the HTTP.publish will mount:
|
||||
* `GET` - `/mylist` *- all published data*
|
||||
|
||||
```js
|
||||
myCollection = new Meteor.Collection('list');
|
||||
|
||||
// Add access points for `GET`
|
||||
HTTP.publish({name: 'mylist'}, function(data) {
|
||||
// this.userId, this.query, this.params
|
||||
return myCollection.find({});
|
||||
});
|
||||
```
|
||||
|
||||
### Create Update Delete only
|
||||
If handed a collection only the HTTP.publish will mount:
|
||||
* `POST` - `/api/list` *- insert a document into collection*
|
||||
* `PUT` - `/api/list/:id` *- update a document*
|
||||
* `DELETE` - `/api/list/:id` *- remove a document*
|
||||
|
||||
```js
|
||||
myCollection = new Meteor.Collection('list');
|
||||
|
||||
// Add access points for `POST`, `PUT`, `DELETE`
|
||||
HTTP.publish({collection: myCollection});
|
||||
```
|
||||
|
||||
## Publish scope
|
||||
The publish scope contains different kinds of inputs. We can also get user details if logged in.
|
||||
|
||||
* `this.userId` The user whos id and token was used to run this method, if set/found
|
||||
* `this.query` - query params `?token=1` -> { token: 1 }
|
||||
* `this.params` - Set params /foo/:name/test/:id -> { name: '', id: '' }
|
||||
|
||||
## Passing data via header
|
||||
From the client:
|
||||
```js
|
||||
HTTP.get('/api/list', {
|
||||
data: { foo: 'bar' }
|
||||
}, function(err, result) {
|
||||
console.log('Content in parsed json: ');
|
||||
console.log(result.data);
|
||||
});
|
||||
```
|
||||
|
||||
HTTP Server method:
|
||||
```js
|
||||
HTTP.publish({collection: myCollection}, function(data) {
|
||||
// data === { foo: 'bar' }
|
||||
});
|
||||
```
|
||||
|
||||
## Authentication
|
||||
For details on authentication of http calls please read the [Authentication part in HTTP.methods package](https://github.com/raix/Meteor-http-methods#authentication)
|
||||
|
||||
*The publish will have the `this.userId` set if an authenticated user is making the request.*
|
||||
|
||||
## Format handlers
|
||||
The query parametre `format` is used to set different output formats. The buildin format is `json` *(EJSON since we are on Meteor)*
|
||||
|
||||
Example: *(`json` is buildin)*
|
||||
```js
|
||||
// Format the output into json
|
||||
HTTP.publishFormats({
|
||||
'json': function(result) {
|
||||
// Set the method scope content type to json
|
||||
this.setContentType('application/json');
|
||||
// Return EJSON string
|
||||
return EJSON.stringify(result);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
`GET` url: `/api/list?format=json`
|
||||
```js
|
||||
HTTP.get('/api/list', {
|
||||
params: {
|
||||
format: 'json'
|
||||
}
|
||||
}, function(err, result) {
|
||||
console.log('Back from update');
|
||||
if (err) {
|
||||
console.log('Got error');
|
||||
}
|
||||
console.log('Got json back: ' + result.content);
|
||||
});
|
||||
```
|
||||
|
||||
## Unpublish
|
||||
For `api` integrity theres added an `HTTP.unpublish` method that takes a collection or name of mount point to remove.
|
||||
|
||||
## API Documentation
|
||||
|
||||
[Here](api.md)
|
||||
153
packages/wekan-cfs-http-publish/api.md
Normal file
153
packages/wekan-cfs-http-publish/api.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
## http-publish Public API ##
|
||||
|
||||
Adds HTTP.publish and HTTP.unpublish RESTful
|
||||
|
||||
_API documentation automatically generated by [docmeteor](https://github.com/raix/docmeteor)._
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP"></a>_publishHTTP {any} <sub><i>Server</i></sub> ###
|
||||
|
||||
```
|
||||
GET /note
|
||||
GET /note/:id
|
||||
POST /note
|
||||
PUT /note/:id
|
||||
DELETE /note/:id
|
||||
```
|
||||
|
||||
> ```_publishHTTP = { ...``` [http.publish.server.api.js:20](http.publish.server.api.js#L20)
|
||||
|
||||
|
||||
|
||||
-
|
||||
Could be cool if we could serve some api doc or even an api script
|
||||
user could do <script href="/note/api?token=1&user=2"></script> and be served
|
||||
a client-side javascript api?
|
||||
Eg.
|
||||
HTTP.api.note.create();
|
||||
HTTP.api.login(username, password);
|
||||
HTTP.api.logout
|
||||
-
|
||||
|
||||
### <a name="HTTP.publishFormats"></a>*http*.publishFormats(newHandlers) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __publishFormats__ is defined in `HTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __newHandlers__ *{Object}*
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Add publish formats. Example:
|
||||
```js
|
||||
HTTP.publishFormats({
|
||||
json: function(inputObject) {
|
||||
// Set the method scope content type to json
|
||||
this.setContentType('application/json');
|
||||
// Return EJSON string
|
||||
return EJSON.stringify(inputObject);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> ```HTTP.publishFormats = function httpPublishFormats(newHandlers) { ...``` [http.publish.server.api.js:215](http.publish.server.api.js#L215)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="HTTP.publish"></a>*http*.publish(options, [name], [collection], [publishFunc]) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __publish__ is defined in `HTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __options__ *{Object}*
|
||||
* __defaultFormat__ *{String}* (Optional, Default = 'json')
|
||||
|
||||
Format to use for responses when `format` is not found in the query string.
|
||||
|
||||
* __collectionGet__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add GET restpoint for collection? Requires a publish function.
|
||||
|
||||
* __collectionPost__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add POST restpoint for adding documents to the collection?
|
||||
|
||||
* __documentGet__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add GET restpoint for documents in collection? Requires a publish function.
|
||||
|
||||
* __documentPut__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add PUT restpoint for updating a document in the collection?
|
||||
|
||||
* __documentDelete__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add DELETE restpoint for deleting a document in the collection?
|
||||
|
||||
* __name__ *{String}* (Optional)
|
||||
|
||||
Restpoint name (url prefix). Optional if `collection` is passed. Will mount on `/api/collectionName` by default.
|
||||
|
||||
* __collection__ *{[Meteor.Collection](#Meteor.Collection)}* (Optional)
|
||||
|
||||
Meteor.Collection instance. Required for all restpoints except collectionGet
|
||||
|
||||
* __publishFunc__ *{Function}* (Optional)
|
||||
|
||||
A publish function. Required to mount GET restpoints.
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Publishes one or more restpoints, mounted on "name" ("/api/collectionName/"
|
||||
by default). The GET restpoints are subscribed to the document set (cursor)
|
||||
returned by the publish function you supply. The other restpoints forward
|
||||
requests to Meteor's built-in DDP methods (insert, update, remove), meaning
|
||||
that full allow/deny security is automatic.
|
||||
|
||||
__Usage:__
|
||||
|
||||
Publish only:
|
||||
|
||||
HTTP.publish({name: 'mypublish'}, publishFunc);
|
||||
|
||||
Publish and mount crud rest point for collection /api/myCollection:
|
||||
|
||||
HTTP.publish({collection: myCollection}, publishFunc);
|
||||
|
||||
Mount CUD rest point for collection and documents without GET:
|
||||
|
||||
HTTP.publish({collection: myCollection});
|
||||
|
||||
|
||||
> ```HTTP.publish = function httpPublish(options, publishFunc) { ...``` [http.publish.server.api.js:256](http.publish.server.api.js#L256)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="HTTP.unpublish"></a>*http*.unpublish([name]) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __unpublish__ is defined in `HTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String|[Meteor.Collection](#Meteor.Collection)}* (Optional)
|
||||
|
||||
The method name or collection
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Unpublishes all HTTP methods that were published with the given name or
|
||||
for the given collection. Call with no arguments to unpublish all.
|
||||
|
||||
> ```HTTP.unpublish = _publishHTTP.unpublish;``` [http.publish.server.api.js:453](http.publish.server.api.js#L453)
|
||||
|
||||
|
||||
12
packages/wekan-cfs-http-publish/http.publish.client.api.js
Normal file
12
packages/wekan-cfs-http-publish/http.publish.client.api.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// Client-side is not implemented
|
||||
HTTP.publish = function() {
|
||||
throw new Error('HTTP.publish not implemented on client-side');
|
||||
};
|
||||
|
||||
HTTP.publishFormats = function() {
|
||||
throw new Error('HTTP.publishFormats not implemented on client-side');
|
||||
};
|
||||
|
||||
HTTP.unpublish = function() {
|
||||
throw new Error('HTTP.unpublish not implemented on client-side');
|
||||
};
|
||||
466
packages/wekan-cfs-http-publish/http.publish.server.api.js
Normal file
466
packages/wekan-cfs-http-publish/http.publish.server.api.js
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
|
||||
GET /note
|
||||
GET /note/:id
|
||||
POST /note
|
||||
PUT /note/:id
|
||||
DELETE /note/:id
|
||||
|
||||
*/
|
||||
|
||||
// Could be cool if we could serve some api doc or even an api script
|
||||
// user could do <script href="/note/api?token=1&user=2"></script> and be served
|
||||
// a client-side javascript api?
|
||||
// Eg.
|
||||
// HTTP.api.note.create();
|
||||
// HTTP.api.login(username, password);
|
||||
// HTTP.api.logout
|
||||
|
||||
|
||||
_publishHTTP = {};
|
||||
|
||||
// Cache the names of all http methods we've published
|
||||
_publishHTTP.currentlyPublished = [];
|
||||
|
||||
var defaultAPIPrefix = '/api/';
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.getPublishScope
|
||||
* @private
|
||||
* @param {Object} scope
|
||||
* @returns {httpPublishGetPublishScope.publishScope}
|
||||
*
|
||||
* Creates a nice scope for the publish method
|
||||
*/
|
||||
_publishHTTP.getPublishScope = function httpPublishGetPublishScope(scope) {
|
||||
var publishScope = {};
|
||||
publishScope.userId = scope.userId;
|
||||
publishScope.params = scope.params;
|
||||
publishScope.query = scope.query;
|
||||
// TODO: Additional scoping
|
||||
// publishScope.added
|
||||
// publishScope.ready
|
||||
return publishScope;
|
||||
};
|
||||
|
||||
_publishHTTP.formatHandlers = {};
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.formatHandlers.json
|
||||
* @private
|
||||
* @param {Object} result - The result object
|
||||
* @returns {String} JSON
|
||||
*
|
||||
* Formats the output into JSON and sets the appropriate content type on `this`
|
||||
*/
|
||||
_publishHTTP.formatHandlers.json = function httpPublishJSONFormatHandler(result) {
|
||||
// Set the method scope content type to json
|
||||
this.setContentType('application/json');
|
||||
// Return EJSON string
|
||||
return EJSON.stringify(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.formatResult
|
||||
* @private
|
||||
* @param {Object} result - The result object
|
||||
* @param {Object} scope
|
||||
* @param {String} [defaultFormat='json'] - Default format to use if format is not in query string.
|
||||
* @returns {Any} The formatted result
|
||||
*
|
||||
* Formats the result into the format selected by querystring eg. "&format=json"
|
||||
*/
|
||||
_publishHTTP.formatResult = function httpPublishFormatResult(result, scope, defaultFormat) {
|
||||
|
||||
// Get the format in lower case and default to json
|
||||
var format = scope && scope.query && scope.query.format || defaultFormat || 'json';
|
||||
|
||||
// Set the format handler found
|
||||
var formatHandlerFound = !!(typeof _publishHTTP.formatHandlers[format] === 'function');
|
||||
|
||||
// Set the format handler and fallback to default json if handler not found
|
||||
var formatHandler = _publishHTTP.formatHandlers[(formatHandlerFound) ? format : 'json'];
|
||||
|
||||
// Check if format handler is a function
|
||||
if (typeof formatHandler !== 'function') {
|
||||
// We break things the user could have overwritten the default json handler
|
||||
throw new Error('The default json format handler not found');
|
||||
}
|
||||
|
||||
if (!formatHandlerFound) {
|
||||
scope.setStatusCode(500);
|
||||
return '{"error":"Format handler for: `' + format + '` not found"}';
|
||||
}
|
||||
|
||||
// Execute the format handler
|
||||
try {
|
||||
return formatHandler.apply(scope, [result]);
|
||||
} catch(err) {
|
||||
scope.setStatusCode(500);
|
||||
return '{"error":"Format handler for: `' + format + '` Error: ' + err.message + '"}';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.error
|
||||
* @private
|
||||
* @param {String} statusCode - The status code
|
||||
* @param {String} message - The message
|
||||
* @param {Object} scope
|
||||
* @returns {Any} The formatted result
|
||||
*
|
||||
* Responds with error message in the expected format
|
||||
*/
|
||||
_publishHTTP.error = function httpPublishError(statusCode, message, scope) {
|
||||
var result = _publishHTTP.formatResult(message, scope);
|
||||
scope.setStatusCode(statusCode);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.getMethodHandler
|
||||
* @private
|
||||
* @param {Meteor.Collection} collection - The Meteor.Collection instance
|
||||
* @param {String} methodName - The method name
|
||||
* @returns {Function} The server method
|
||||
*
|
||||
* Returns the DDP connection handler, already setup and secured
|
||||
*/
|
||||
_publishHTTP.getMethodHandler = function httpPublishGetMethodHandler(collection, methodName) {
|
||||
if (collection instanceof Meteor.Collection) {
|
||||
if (collection._connection && collection._connection.method_handlers) {
|
||||
return collection._connection.method_handlers[collection._prefix + methodName];
|
||||
} else {
|
||||
throw new Error('HTTP publish does not work with current version of Meteor');
|
||||
}
|
||||
} else {
|
||||
throw new Error('_publishHTTP.getMethodHandler expected a collection');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.unpublishList
|
||||
* @private
|
||||
* @param {Array} names - List of method names to unpublish
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Unpublishes all HTTP methods that have names matching the given list.
|
||||
*/
|
||||
_publishHTTP.unpublishList = function httpPublishUnpublishList(names) {
|
||||
if (!names.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Carry object for methods
|
||||
var methods = {};
|
||||
|
||||
// Unpublish the rest points by setting them to false
|
||||
for (var i = 0, ln = names.length; i < ln; i++) {
|
||||
methods[names[i]] = false;
|
||||
}
|
||||
|
||||
HTTP.methods(methods);
|
||||
|
||||
// Remove the names from our list of currently published methods
|
||||
_publishHTTP.currentlyPublished = _.difference(_publishHTTP.currentlyPublished, names);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _publishHTTP.unpublish
|
||||
* @private
|
||||
* @param {String|Meteor.Collection} [name] - The method name or collection
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Unpublishes all HTTP methods that were published with the given name or
|
||||
* for the given collection. Call with no arguments to unpublish all.
|
||||
*/
|
||||
_publishHTTP.unpublish = function httpPublishUnpublish(/* name or collection, options */) {
|
||||
|
||||
// Determine what method name we're unpublishing
|
||||
var name = (arguments[0] instanceof Meteor.Collection) ?
|
||||
defaultAPIPrefix + arguments[0]._name : arguments[0];
|
||||
|
||||
// Unpublish name and name/id
|
||||
if (name && name.length) {
|
||||
_publishHTTP.unpublishList([name, name + '/:id']);
|
||||
}
|
||||
|
||||
// If no args, unpublish all
|
||||
else {
|
||||
_publishHTTP.unpublishList(_publishHTTP.currentlyPublished);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @method HTTP.publishFormats
|
||||
* @public
|
||||
* @param {Object} newHandlers
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Add publish formats. Example:
|
||||
```js
|
||||
HTTP.publishFormats({
|
||||
|
||||
json: function(inputObject) {
|
||||
// Set the method scope content type to json
|
||||
this.setContentType('application/json');
|
||||
// Return EJSON string
|
||||
return EJSON.stringify(inputObject);
|
||||
}
|
||||
|
||||
});
|
||||
```
|
||||
*/
|
||||
HTTP.publishFormats = function httpPublishFormats(newHandlers) {
|
||||
_.extend(_publishHTTP.formatHandlers, newHandlers);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method HTTP.publish
|
||||
* @public
|
||||
* @param {Object} options
|
||||
* @param {String} [name] - Restpoint name (url prefix). Optional if `collection` is passed. Will mount on `/api/collectionName` by default.
|
||||
* @param {Meteor.Collection} [collection] - Meteor.Collection instance. Required for all restpoints except collectionGet
|
||||
* @param {String} [options.defaultFormat='json'] - Format to use for responses when `format` is not found in the query string.
|
||||
* @param {String} [options.collectionGet=true] - Add GET restpoint for collection? Requires a publish function.
|
||||
* @param {String} [options.collectionPost=true] - Add POST restpoint for adding documents to the collection?
|
||||
* @param {String} [options.documentGet=true] - Add GET restpoint for documents in collection? Requires a publish function.
|
||||
* @param {String} [options.documentPut=true] - Add PUT restpoint for updating a document in the collection?
|
||||
* @param {String} [options.documentDelete=true] - Add DELETE restpoint for deleting a document in the collection?
|
||||
* @param {Function} [publishFunc] - A publish function. Required to mount GET restpoints.
|
||||
* @returns {undefined}
|
||||
* @todo this should use options argument instead of optional args
|
||||
*
|
||||
* Publishes one or more restpoints, mounted on "name" ("/api/collectionName/"
|
||||
* by default). The GET restpoints are subscribed to the document set (cursor)
|
||||
* returned by the publish function you supply. The other restpoints forward
|
||||
* requests to Meteor's built-in DDP methods (insert, update, remove), meaning
|
||||
* that full allow/deny security is automatic.
|
||||
*
|
||||
* __Usage:__
|
||||
*
|
||||
* Publish only:
|
||||
*
|
||||
* HTTP.publish({name: 'mypublish'}, publishFunc);
|
||||
*
|
||||
* Publish and mount crud rest point for collection /api/myCollection:
|
||||
*
|
||||
* HTTP.publish({collection: myCollection}, publishFunc);
|
||||
*
|
||||
* Mount CUD rest point for collection and documents without GET:
|
||||
*
|
||||
* HTTP.publish({collection: myCollection});
|
||||
*
|
||||
*/
|
||||
HTTP.publish = function httpPublish(options, publishFunc) {
|
||||
options = _.extend({
|
||||
name: null,
|
||||
auth: null,
|
||||
collection: null,
|
||||
defaultFormat: null,
|
||||
collectionGet: true,
|
||||
collectionPost: true,
|
||||
documentGet: true,
|
||||
documentPut: true,
|
||||
documentDelete: true
|
||||
}, options || {});
|
||||
|
||||
var collection = options.collection;
|
||||
|
||||
// Use provided name or build one
|
||||
var name = (typeof options.name === "string") ? options.name : defaultAPIPrefix + collection._name;
|
||||
|
||||
// Make sure we have a name
|
||||
if (typeof name !== "string") {
|
||||
throw new Error('HTTP.publish expected a collection or name option');
|
||||
}
|
||||
|
||||
var defaultFormat = options.defaultFormat;
|
||||
|
||||
// Rig the methods for the CRUD interface
|
||||
var methods = {};
|
||||
|
||||
// console.log('HTTP restpoint: ' + name);
|
||||
|
||||
// list and create
|
||||
methods[name] = {};
|
||||
|
||||
if (options.collectionGet && publishFunc) {
|
||||
// Return the published documents
|
||||
methods[name].get = function(data) {
|
||||
// Format the scope for the publish method
|
||||
var publishScope = _publishHTTP.getPublishScope(this);
|
||||
// Get the publish cursor
|
||||
var cursor = publishFunc.apply(publishScope, [data]);
|
||||
|
||||
// Check if its a cursor
|
||||
if (cursor && cursor.fetch) {
|
||||
// Fetch the data fron cursor
|
||||
var result = cursor.fetch();
|
||||
// Return the data
|
||||
return _publishHTTP.formatResult(result, this, defaultFormat);
|
||||
} else {
|
||||
// We didnt get any
|
||||
return _publishHTTP.error(200, [], this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (collection) {
|
||||
// If we have a collection then add insert method
|
||||
if (options.collectionPost) {
|
||||
methods[name].post = function(data) {
|
||||
var insertMethodHandler = _publishHTTP.getMethodHandler(collection, 'insert');
|
||||
// Make sure that _id isset else create a Meteor id
|
||||
data._id = data._id || Random.id();
|
||||
// Create the document
|
||||
try {
|
||||
// We should be passed a document in data
|
||||
insertMethodHandler.apply(this, [data]);
|
||||
// Return the data
|
||||
return _publishHTTP.formatResult({ _id: data._id }, this, defaultFormat);
|
||||
} catch(err) {
|
||||
// This would be a Meteor.error?
|
||||
return _publishHTTP.error(err.error, { error: err.message }, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// We also add the findOne, update and remove methods
|
||||
methods[name + '/:id'] = {};
|
||||
|
||||
if (options.documentGet && publishFunc) {
|
||||
// We have to have a publish method inorder to publish id? The user could
|
||||
// just write a publish all if needed - better to make this explicit
|
||||
methods[name + '/:id'].get = function(data) {
|
||||
// Get the mongoId
|
||||
var mongoId = this.params.id;
|
||||
|
||||
// We would allways expect a string but it could be empty
|
||||
if (mongoId !== '') {
|
||||
|
||||
// Format the scope for the publish method
|
||||
var publishScope = _publishHTTP.getPublishScope(this);
|
||||
|
||||
// Get the publish cursor
|
||||
var cursor = publishFunc.apply(publishScope, [data]);
|
||||
|
||||
// Result will contain the document if found
|
||||
var result;
|
||||
|
||||
// Check to see if document is in published cursor
|
||||
if (cursor) {
|
||||
cursor.forEach(function(doc) {
|
||||
if (!result) {
|
||||
if (doc._id === mongoId) {
|
||||
result = doc;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If the document is found the return
|
||||
if (result) {
|
||||
return _publishHTTP.formatResult(result, this, defaultFormat);
|
||||
} else {
|
||||
// We do a check to see if the doc id exists
|
||||
var exists = collection.findOne({ _id: mongoId });
|
||||
// If it exists its not published to the user
|
||||
if (exists) {
|
||||
// Unauthorized
|
||||
return _publishHTTP.error(401, { error: 'Unauthorized' }, this);
|
||||
} else {
|
||||
// Not found
|
||||
return _publishHTTP.error(404, { error: 'Document with id ' + mongoId + ' not found' }, this);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
return _publishHTTP.error(400, { error: 'Method expected a document id' }, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (options.documentPut) {
|
||||
methods[name + '/:id'].put = function(data) {
|
||||
// Get the mongoId
|
||||
var mongoId = this.params.id;
|
||||
|
||||
// We would allways expect a string but it could be empty
|
||||
if (mongoId !== '') {
|
||||
|
||||
var updateMethodHandler = _publishHTTP.getMethodHandler(collection, 'update');
|
||||
// Create the document
|
||||
try {
|
||||
// We should be passed a document in data
|
||||
updateMethodHandler.apply(this, [{ _id: mongoId }, data]);
|
||||
// Return the data
|
||||
return _publishHTTP.formatResult({ _id: mongoId }, this, defaultFormat);
|
||||
} catch(err) {
|
||||
// This would be a Meteor.error?
|
||||
return _publishHTTP.error(err.error, { error: err.message }, this);
|
||||
}
|
||||
|
||||
} else {
|
||||
return _publishHTTP.error(400, { error: 'Method expected a document id' }, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (options.documentDelete) {
|
||||
methods[name + '/:id'].delete = function(data) {
|
||||
// Get the mongoId
|
||||
var mongoId = this.params.id;
|
||||
|
||||
// We would allways expect a string but it could be empty
|
||||
if (mongoId !== '') {
|
||||
|
||||
var removeMethodHandler = _publishHTTP.getMethodHandler(collection, 'remove');
|
||||
// Create the document
|
||||
try {
|
||||
// We should be passed a document in data
|
||||
removeMethodHandler.apply(this, [{ _id: mongoId }]);
|
||||
// Return the data
|
||||
return _publishHTTP.formatResult({ _id: mongoId }, this, defaultFormat);
|
||||
} catch(err) {
|
||||
// This would be a Meteor.error?
|
||||
return _publishHTTP.error(err.error, { error: err.message }, this);
|
||||
}
|
||||
|
||||
} else {
|
||||
return _publishHTTP.error(400, { error: 'Method expected a document id' }, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Authenticate with our own auth method: https://github.com/zcfs/Meteor-http-methods#authentication
|
||||
if (options.auth) {
|
||||
if (methods[name]) {
|
||||
methods[name].auth = options.auth;
|
||||
}
|
||||
if (methods[name + '/:id']) {
|
||||
methods[name + '/:id'].auth = options.auth;
|
||||
}
|
||||
}
|
||||
|
||||
// Publish the methods
|
||||
HTTP.methods(methods);
|
||||
|
||||
// Mark these method names as currently published
|
||||
_publishHTTP.currentlyPublished = _.union(_publishHTTP.currentlyPublished, _.keys(methods));
|
||||
|
||||
}; // EO Publish
|
||||
|
||||
/**
|
||||
* @method HTTP.unpublish
|
||||
* @public
|
||||
* @param {String|Meteor.Collection} [name] - The method name or collection
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Unpublishes all HTTP methods that were published with the given name or
|
||||
* for the given collection. Call with no arguments to unpublish all.
|
||||
*/
|
||||
HTTP.unpublish = _publishHTTP.unpublish;
|
||||
175
packages/wekan-cfs-http-publish/http.publish.tests.client.js
Normal file
175
packages/wekan-cfs-http-publish/http.publish.tests.client.js
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
function equals(a, b) {
|
||||
return !!(EJSON.stringify(a) === EJSON.stringify(b));
|
||||
}
|
||||
|
||||
list = new Meteor.Collection('list');
|
||||
console.log('Client url: ' + Meteor.absoluteUrl('api'));
|
||||
|
||||
Tinytest.add('http-publish - client - test environment', function(test) {
|
||||
test.isTrue(typeof _publishHTTP === 'undefined', 'test environment not initialized _publishHTTP');
|
||||
test.isTrue(typeof HTTP !== 'undefined', 'test environment not initialized HTTP');
|
||||
test.isTrue(typeof HTTP.publish !== 'undefined', 'test environment not initialized HTTP.publish');
|
||||
test.isTrue(typeof HTTP.unpublish !== 'undefined', 'test environment not initialized HTTP.unpublish');
|
||||
test.isTrue(typeof HTTP.publishFormats !== 'undefined', 'test environment not initialized HTTP.publishFormats');
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - clearTest', function (test, onComplete) {
|
||||
test.isTrue(true);
|
||||
Meteor.call('clearTest', function(err, result) {
|
||||
test.isTrue(result);
|
||||
onComplete();
|
||||
});
|
||||
test.isTrue(true);
|
||||
});
|
||||
|
||||
id = '';
|
||||
removedId = '';
|
||||
|
||||
Tinytest.addAsync('http-publish - client - get list', function (test, onComplete) {
|
||||
|
||||
HTTP.get(Meteor.absoluteUrl('api/list'), function(err, result) {
|
||||
// Test the length of array result
|
||||
var len = result.data && result.data.length;
|
||||
test.isTrue(!!len, 'Result was empty');
|
||||
// Get the object
|
||||
var obj = result.data && result.data[0] || {};
|
||||
test.equal(obj.text, 'OK', 'Didnt get the expected result');
|
||||
// Set the id for the next test
|
||||
id = obj._id;
|
||||
onComplete();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - get list from custom prefix', function (test, onComplete) {
|
||||
|
||||
// Now test the one we added with a custom prefix
|
||||
HTTP.get(Meteor.absoluteUrl('api2/list'), function(err, result) {
|
||||
// Test the length of array result
|
||||
var len = result.data && result.data.length;
|
||||
test.isTrue(!!len, 'Result was empty');
|
||||
// Get the object
|
||||
var obj = result.data && result.data[0] || {};
|
||||
test.equal(obj.text, 'OK', 'Didnt get the expected result');
|
||||
onComplete();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - unmountCustom', function (test, onComplete) {
|
||||
// Now unmount the methods with custom prefix
|
||||
test.isTrue(true);
|
||||
Meteor.call('unmountCustom', function(err, result) {
|
||||
test.isTrue(result);
|
||||
onComplete();
|
||||
});
|
||||
test.isTrue(true);
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - custom unmounted', function (test, onComplete) {
|
||||
|
||||
// Now test the one we added with a custom prefix
|
||||
HTTP.get(Meteor.absoluteUrl('api2/list'), function(err, result) {
|
||||
test.isTrue(!!err, "Should have received an error since we unmounted the custom rest points");
|
||||
onComplete();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - put list', function (test, onComplete) {
|
||||
|
||||
test.isTrue(id !== '', 'No id is set?');
|
||||
|
||||
// Update the data
|
||||
HTTP.put(Meteor.absoluteUrl('api/list/' + id), {
|
||||
data: {
|
||||
$set: { text: 'UPDATED' }
|
||||
}
|
||||
}, function(err, result) {
|
||||
var resultId = result.data && result.data._id;
|
||||
test.isTrue(resultId !== undefined, 'Didnt get the expected id in result');
|
||||
|
||||
// Check if data is updated
|
||||
HTTP.get(Meteor.absoluteUrl('api/list'), function(err, result) {
|
||||
var len = result.data && result.data.length;
|
||||
test.isTrue(!!len, 'Result was empty');
|
||||
var obj = result.data && result.data[0] || {};
|
||||
test.equal(obj.text, 'UPDATED', 'Didnt get the expected result');
|
||||
onComplete();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - insert/remove list', function (test, onComplete) {
|
||||
|
||||
// Insert a doc
|
||||
HTTP.post(Meteor.absoluteUrl('api/list'), {
|
||||
data: {
|
||||
text: 'INSERTED'
|
||||
}
|
||||
}, function(err, result) {
|
||||
var resultId = result.data && result.data._id;
|
||||
test.isTrue(resultId !== undefined, 'Didnt get the expected id in result');
|
||||
// Delete the doc
|
||||
HTTP.del(Meteor.absoluteUrl('api/list/' + resultId), function(err, result) {
|
||||
removedId = result.data && result.data._id;
|
||||
test.isTrue(removedId !== undefined, 'Didnt get the expected id in result');
|
||||
onComplete();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - check removed', function (test, onComplete) {
|
||||
|
||||
test.isTrue(removedId !== '', 'No removedId is set?');
|
||||
|
||||
HTTP.get(Meteor.absoluteUrl('api/list/' + removedId), function(err, result) {
|
||||
var obj = result.data || {};
|
||||
test.isTrue(obj._id === undefined, 'Item was not removed');
|
||||
test.isTrue(err.response.statusCode === 404, 'Item was not removed');
|
||||
onComplete();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Tinytest.addAsync('http-publish - client - check findOne', function (test, onComplete) {
|
||||
|
||||
test.isTrue(id !== '', 'No id is set?');
|
||||
|
||||
HTTP.get(Meteor.absoluteUrl('api/list/' + id), function(err, result) {
|
||||
var obj = result.data || {};
|
||||
test.isTrue(obj._id !== undefined, 'expected a document');
|
||||
test.isTrue(obj.text === 'UPDATED', 'expected text === UPDATED');
|
||||
|
||||
onComplete();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Check if removedId found
|
||||
|
||||
// Check if id still found
|
||||
|
||||
|
||||
//Test API:
|
||||
//test.isFalse(v, msg)
|
||||
//test.isTrue(v, msg)
|
||||
//test.equalactual, expected, message, not
|
||||
//test.length(obj, len)
|
||||
//test.include(s, v)
|
||||
//test.isNaN(v, msg)
|
||||
//test.isUndefined(v, msg)
|
||||
//test.isNotNull
|
||||
//test.isNull
|
||||
//test.throws(func)
|
||||
//test.instanceOf(obj, klass)
|
||||
//test.notEqual(actual, expected, message)
|
||||
//test.runId()
|
||||
//test.exception(exception)
|
||||
//test.expect_fail()
|
||||
//test.ok(doc)
|
||||
//test.fail(doc)
|
||||
//test.equal(a, b, msg)
|
||||
149
packages/wekan-cfs-http-publish/http.publish.tests.server.js
Normal file
149
packages/wekan-cfs-http-publish/http.publish.tests.server.js
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
function equals(a, b) {
|
||||
return !!(EJSON.stringify(a) === EJSON.stringify(b));
|
||||
}
|
||||
|
||||
Tinytest.add('http-publish - server - test environment', function(test) {
|
||||
test.isTrue(typeof _publishHTTP !== 'undefined', 'test environment not initialized _publishHTTP');
|
||||
test.isTrue(typeof HTTP !== 'undefined', 'test environment not initialized HTTP');
|
||||
test.isTrue(typeof HTTP.publish !== 'undefined', 'test environment not initialized HTTP.publish');
|
||||
test.isTrue(typeof HTTP.unpublish !== 'undefined', 'test environment not initialized HTTP.unpublish');
|
||||
test.isTrue(typeof HTTP.publishFormats !== 'undefined', 'test environment not initialized HTTP.publishFormats');
|
||||
|
||||
});
|
||||
|
||||
list = new Meteor.Collection('list');
|
||||
console.log('Server url: ' + Meteor.absoluteUrl());
|
||||
|
||||
list.allow({
|
||||
insert: function() { return true; },
|
||||
update: function() { return true; },
|
||||
remove: function() { return true; }
|
||||
});
|
||||
|
||||
console.log('Rig publish');
|
||||
HTTP.publish({collection: list}, function() {
|
||||
return list.find();
|
||||
});
|
||||
|
||||
// Test custom prefix, too
|
||||
HTTP.publish({collection: list, name: '/api2/list'}, function() {
|
||||
return list.find();
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
clearTest: function() {
|
||||
console.log('Client called clearTest');
|
||||
// Empty test db
|
||||
list.remove({});
|
||||
|
||||
// Insert one text
|
||||
list.insert({ text: 'OK' });
|
||||
|
||||
// Count
|
||||
var count = list.find().count();
|
||||
|
||||
return !!(count === 1);
|
||||
},
|
||||
unmountCustom: function() {
|
||||
console.log('Client called unmountCustom');
|
||||
_publishHTTP.unpublish('/api2/list');
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Tinytest.add('http-publish - server - getMethodHandler', function(test) {
|
||||
|
||||
try {
|
||||
var methodHandler = _publishHTTP.getMethodHandler(list, 'insert');
|
||||
|
||||
test.isTrue(typeof methodHandler === 'function', 'expected getMethodHandler to return a function');
|
||||
|
||||
} catch(err) {
|
||||
test.fail(err.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
Tinytest.add('http-publish - server - formatHandlers', function(test) {
|
||||
|
||||
test.isTrue(typeof _publishHTTP.formatHandlers.json === 'function', 'Cant find formatHandler for json');
|
||||
|
||||
var testScope = {
|
||||
code: 0,
|
||||
setContentType: function(code) {
|
||||
this.code = code;
|
||||
}
|
||||
};
|
||||
var resultFormatHandler = _publishHTTP.formatHandlers.json.apply(testScope, [{test:'ok'}]);
|
||||
|
||||
test.equal(testScope.code, 'application/json', 'json formatHandler have not set setContentType');
|
||||
|
||||
test.equal(resultFormatHandler, '{"test":"ok"}', 'json formatHandler returned a bad result');
|
||||
|
||||
});
|
||||
|
||||
Tinytest.add('http-publish - server - getPublishScope', function(test) {
|
||||
|
||||
var oldScope = {
|
||||
userId: '1',
|
||||
params: '2',
|
||||
query: '3',
|
||||
oldStuff: 'hmmm'
|
||||
};
|
||||
|
||||
var newScope = _publishHTTP.getPublishScope(oldScope);
|
||||
|
||||
test.isUndefined(newScope.oldStuff, 'This oldStuff should not be in the new scope');
|
||||
|
||||
test.equal(newScope.userId, '1', 'userId not set in the new scope');
|
||||
test.equal(newScope.params, '2', 'params not set in the new scope');
|
||||
test.equal(newScope.query, '3', 'query not set in the new scope');
|
||||
|
||||
});
|
||||
|
||||
Tinytest.add('http-publish - server - formatResult', function(test) {
|
||||
|
||||
var oldScope = {
|
||||
statusCode: 200,
|
||||
userId: '1',
|
||||
params: '2',
|
||||
query: '3',
|
||||
oldStuff: 'hmmm',
|
||||
setStatusCode: function(code) {
|
||||
this.statusCode = code;
|
||||
},
|
||||
code: 0,
|
||||
setContentType: function(code) {
|
||||
this.code = code;
|
||||
}
|
||||
};
|
||||
|
||||
var result = _publishHTTP.formatResult({test: 'ok'}, oldScope);
|
||||
|
||||
test.equal(oldScope.code, 'application/json', 'json formatHandler have not set setContentType');
|
||||
|
||||
test.equal(result, '{"test":"ok"}', 'json formatHandler returned a bad result');
|
||||
|
||||
});
|
||||
|
||||
//Test API:
|
||||
//test.isFalse(v, msg)
|
||||
//test.isTrue(v, msg)
|
||||
//test.equalactual, expected, message, not
|
||||
//test.length(obj, len)
|
||||
//test.include(s, v)
|
||||
//test.isNaN(v, msg)
|
||||
//test.isUndefined(v, msg)
|
||||
//test.isNotNull
|
||||
//test.isNull
|
||||
//test.throws(func)
|
||||
//test.instanceOf(obj, klass)
|
||||
//test.notEqual(actual, expected, message)
|
||||
//test.runId()
|
||||
//test.exception(exception)
|
||||
//test.expect_fail()
|
||||
//test.ok(doc)
|
||||
//test.fail(doc)
|
||||
//test.equal(a, b, msg)
|
||||
330
packages/wekan-cfs-http-publish/internal.api.md
Normal file
330
packages/wekan-cfs-http-publish/internal.api.md
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
## Public and Private API ##
|
||||
|
||||
_API documentation automatically generated by [docmeteor](https://github.com/raix/docmeteor)._
|
||||
|
||||
***
|
||||
|
||||
__File: ["http.publish.server.api.js"](http.publish.server.api.js) Where: {server}__
|
||||
|
||||
***
|
||||
|
||||
### <a name="_publishHTTP"></a>_publishHTTP {any} <sub><i>Server</i></sub> ###
|
||||
|
||||
```
|
||||
GET /note
|
||||
GET /note/:id
|
||||
POST /note
|
||||
PUT /note/:id
|
||||
DELETE /note/:id
|
||||
```
|
||||
|
||||
> ```_publishHTTP = { ...``` [http.publish.server.api.js:20](http.publish.server.api.js#L20)
|
||||
|
||||
|
||||
|
||||
-
|
||||
Could be cool if we could serve some api doc or even an api script
|
||||
user could do <script href="/note/api?token=1&user=2"></script> and be served
|
||||
a client-side javascript api?
|
||||
Eg.
|
||||
HTTP.api.note.create();
|
||||
HTTP.api.login(username, password);
|
||||
HTTP.api.logout
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.getPublishScope"></a>*_publishhttp*.getPublishScope(scope) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __getPublishScope__ is defined in `_publishHTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __scope__ *{Object}*
|
||||
|
||||
__Returns__ *{httpPublishGetPublishScope.publishScope}*
|
||||
|
||||
|
||||
Creates a nice scope for the publish method
|
||||
|
||||
> ```_publishHTTP.getPublishScope = function httpPublishGetPublishScope(scope) { ...``` [http.publish.server.api.js:35](http.publish.server.api.js#L35)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.formatHandlers.json"></a>*_publishhttpformathandlers*.json(result) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __json__ is defined in `_publishHTTP.formatHandlers`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __result__ *{Object}*
|
||||
|
||||
The result object
|
||||
|
||||
|
||||
__Returns__ *{String}*
|
||||
JSON
|
||||
|
||||
|
||||
Formats the output into JSON and sets the appropriate content type on `this`
|
||||
|
||||
> ```_publishHTTP.formatHandlers.json = function httpPublishJSONFormatHandler(result) { ...``` [http.publish.server.api.js:56](http.publish.server.api.js#L56)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.formatResult"></a>*_publishhttp*.formatResult(result, scope, [defaultFormat]) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __formatResult__ is defined in `_publishHTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __result__ *{Object}*
|
||||
|
||||
The result object
|
||||
|
||||
* __scope__ *{Object}*
|
||||
* __defaultFormat__ *{String}* (Optional, Default = 'json')
|
||||
|
||||
Default format to use if format is not in query string.
|
||||
|
||||
|
||||
__Returns__ *{Any}*
|
||||
The formatted result
|
||||
|
||||
|
||||
Formats the result into the format selected by querystring eg. "&format=json"
|
||||
|
||||
> ```_publishHTTP.formatResult = function httpPublishFormatResult(result, scope, defaultFormat) { ...``` [http.publish.server.api.js:73](http.publish.server.api.js#L73)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.error"></a>*_publishhttp*.error(statusCode, message, scope) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __error__ is defined in `_publishHTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __statusCode__ *{String}*
|
||||
|
||||
The status code
|
||||
|
||||
* __message__ *{String}*
|
||||
|
||||
The message
|
||||
|
||||
* __scope__ *{Object}*
|
||||
|
||||
__Returns__ *{Any}*
|
||||
The formatted result
|
||||
|
||||
|
||||
Responds with error message in the expected format
|
||||
|
||||
> ```_publishHTTP.error = function httpPublishError(statusCode, message, scope) { ...``` [http.publish.server.api.js:114](http.publish.server.api.js#L114)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.getMethodHandler"></a>*_publishhttp*.getMethodHandler(collection, methodName) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __getMethodHandler__ is defined in `_publishHTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __collection__ *{[Meteor.Collection](#Meteor.Collection)}*
|
||||
|
||||
The Meteor.Collection instance
|
||||
|
||||
* __methodName__ *{String}*
|
||||
|
||||
The method name
|
||||
|
||||
|
||||
__Returns__ *{Function}*
|
||||
The server method
|
||||
|
||||
|
||||
Returns the DDP connection handler, already setup and secured
|
||||
|
||||
> ```_publishHTTP.getMethodHandler = function httpPublishGetMethodHandler(collection, methodName) { ...``` [http.publish.server.api.js:129](http.publish.server.api.js#L129)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.unpublishList"></a>*_publishhttp*.unpublishList(names) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __unpublishList__ is defined in `_publishHTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __names__ *{Array}*
|
||||
|
||||
List of method names to unpublish
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Unpublishes all HTTP methods that have names matching the given list.
|
||||
|
||||
> ```_publishHTTP.unpublishList = function httpPublishUnpublishList(names) { ...``` [http.publish.server.api.js:149](http.publish.server.api.js#L149)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="_publishHTTP.unpublish"></a>*_publishhttp*.unpublish([name]) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __unpublish__ is defined in `_publishHTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String|[Meteor.Collection](#Meteor.Collection)}* (Optional)
|
||||
|
||||
The method name or collection
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Unpublishes all HTTP methods that were published with the given name or
|
||||
for the given collection. Call with no arguments to unpublish all.
|
||||
|
||||
> ```_publishHTTP.unpublish = function httpPublishUnpublish(``` [http.publish.server.api.js:177](http.publish.server.api.js#L177)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="HTTP.publishFormats"></a>*http*.publishFormats(newHandlers) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __publishFormats__ is defined in `HTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __newHandlers__ *{Object}*
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Add publish formats. Example:
|
||||
```js
|
||||
HTTP.publishFormats({
|
||||
json: function(inputObject) {
|
||||
// Set the method scope content type to json
|
||||
this.setContentType('application/json');
|
||||
// Return EJSON string
|
||||
return EJSON.stringify(inputObject);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> ```HTTP.publishFormats = function httpPublishFormats(newHandlers) { ...``` [http.publish.server.api.js:215](http.publish.server.api.js#L215)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="HTTP.publish"></a>*http*.publish(options, [name], [collection], [publishFunc]) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __publish__ is defined in `HTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __options__ *{Object}*
|
||||
* __defaultFormat__ *{String}* (Optional, Default = 'json')
|
||||
|
||||
Format to use for responses when `format` is not found in the query string.
|
||||
|
||||
* __collectionGet__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add GET restpoint for collection? Requires a publish function.
|
||||
|
||||
* __collectionPost__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add POST restpoint for adding documents to the collection?
|
||||
|
||||
* __documentGet__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add GET restpoint for documents in collection? Requires a publish function.
|
||||
|
||||
* __documentPut__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add PUT restpoint for updating a document in the collection?
|
||||
|
||||
* __documentDelete__ *{String}* (Optional, Default = true)
|
||||
|
||||
Add DELETE restpoint for deleting a document in the collection?
|
||||
|
||||
* __name__ *{String}* (Optional)
|
||||
|
||||
Restpoint name (url prefix). Optional if `collection` is passed. Will mount on `/api/collectionName` by default.
|
||||
|
||||
* __collection__ *{[Meteor.Collection](#Meteor.Collection)}* (Optional)
|
||||
|
||||
Meteor.Collection instance. Required for all restpoints except collectionGet
|
||||
|
||||
* __publishFunc__ *{Function}* (Optional)
|
||||
|
||||
A publish function. Required to mount GET restpoints.
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
__TODO__
|
||||
```
|
||||
* this should use options argument instead of optional args
|
||||
```
|
||||
|
||||
|
||||
Publishes one or more restpoints, mounted on "name" ("/api/collectionName/"
|
||||
by default). The GET restpoints are subscribed to the document set (cursor)
|
||||
returned by the publish function you supply. The other restpoints forward
|
||||
requests to Meteor's built-in DDP methods (insert, update, remove), meaning
|
||||
that full allow/deny security is automatic.
|
||||
|
||||
__Usage:__
|
||||
|
||||
Publish only:
|
||||
|
||||
HTTP.publish({name: 'mypublish'}, publishFunc);
|
||||
|
||||
Publish and mount crud rest point for collection /api/myCollection:
|
||||
|
||||
HTTP.publish({collection: myCollection}, publishFunc);
|
||||
|
||||
Mount CUD rest point for collection and documents without GET:
|
||||
|
||||
HTTP.publish({collection: myCollection});
|
||||
|
||||
|
||||
> ```HTTP.publish = function httpPublish(options, publishFunc) { ...``` [http.publish.server.api.js:256](http.publish.server.api.js#L256)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="HTTP.unpublish"></a>*http*.unpublish([name]) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __unpublish__ is defined in `HTTP`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String|[Meteor.Collection](#Meteor.Collection)}* (Optional)
|
||||
|
||||
The method name or collection
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Unpublishes all HTTP methods that were published with the given name or
|
||||
for the given collection. Call with no arguments to unpublish all.
|
||||
|
||||
> ```HTTP.unpublish = _publishHTTP.unpublish;``` [http.publish.server.api.js:453](http.publish.server.api.js#L453)
|
||||
|
||||
|
||||
34
packages/wekan-cfs-http-publish/package.js
Normal file
34
packages/wekan-cfs-http-publish/package.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
Package.describe({
|
||||
git: 'https://github.com/zcfs/Meteor-http-publish.git',
|
||||
name: 'wekan-cfs-http-publish',
|
||||
version: '0.0.13',
|
||||
summary: 'Adds HTTP.publish and HTTP.unpublish RESTful'
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('1.0');
|
||||
|
||||
api.use(['webapp', 'underscore', 'ejson', 'random'], 'server');
|
||||
|
||||
api.use('wekan-cfs-http-methods@0.0.27');
|
||||
|
||||
api.imply && api.imply('wekan-cfs-http-methods');
|
||||
|
||||
api.export && api.export('_publishHTTP', { testOnly: true });
|
||||
|
||||
api.addFiles('http.publish.client.api.js', 'client');
|
||||
api.addFiles('http.publish.server.api.js', 'server');
|
||||
|
||||
});
|
||||
|
||||
Package.onTest(function (api) {
|
||||
api.use('wekan-cfs-http-publish', ['client', 'server']);
|
||||
api.use('test-helpers', ['client', 'server']);
|
||||
api.use('http', 'client');
|
||||
|
||||
api.use(['tinytest', 'underscore', 'ejson', 'ordered-dict',
|
||||
'random', 'deps']);
|
||||
|
||||
api.addFiles('http.publish.tests.server.js', 'server');
|
||||
api.addFiles('http.publish.tests.client.js', 'client');
|
||||
});
|
||||
2
packages/wekan-cfs-http-publish/packages/.gitignore
vendored
Normal file
2
packages/wekan-cfs-http-publish/packages/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/http-publish
|
||||
/http-methods
|
||||
Loading…
Add table
Add a link
Reference in a new issue