| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 | 
							- /*
 
- 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;
 
 
  |