Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adding RPC hooks

  • Loading branch information...
commit 3fffac0a3eb0b19d94afe7a0452c5381af6d94dc 1 parent 02a1858
@mauritslamers authored
View
33 clients/SproutCore/ONRDataSource.js
@@ -366,6 +366,39 @@ SC.ONRDataSource = SC.DataSource.extend({
if(data.createRecordError) this.onCreateRecordError(data);
if(data.updateRecordError) this.onUpdateRecordError(data);
if(data.deleteRecordError) this.onDeleteRecordError(data);
+ if(data.rpcResult) this.onRPCResult(data);
+ if(data.rpcError) this.onRPCError(data);
+ },
+
+ _rpcRequestCache: null,
+
+ rpcRequest: function(functionName,params,callback){
+ // generate an RPC request to ONR
+ var cacheKey = this._createRequestCacheKey();
+ if(!this._rpcRequestCache) this._rpcRequestCache = {};
+ if(!this._rpcRequestCache[cacheKey]) this._rpcRequestCache[cacheKey] = { callback: callback };
+ this.send( { rpcRequest: { functionName: functionName, params: params, returnData: { rpcCacheKey: cacheKey } }});
+ },
+
+ onRPCResult: function(data){
+ if(!this._rpcRequestCache) throw "ONRDataSource: received an RPC onRPC result but no request has been sent";
+ else {
+ var rpcResult = data.rpcResult;
+ if(rpcResult){
+ var cacheKey = rpcResult.returnData.cacheKey;
+ this._rpcRequestCache[rpcResult.cacheKey].callback(rpcResult);
+ }
+ else throw "ONRDataSource: received an invalid rpcResult message";
+ }
+ },
+
+ onRPCError: function(data){
+ if(!this._rpcRequestCache) throw "ONRDataSource: received an RPC onRPC error but no request has been sent";
+ else {
+ var rpcError = data.rpcError;
+ var cacheKey = rpcResult.returnData.cacheKey;
+ this._rpcRequestCache[rpcResult.cacheKey].callback(rpcError);
+ }
},
/*
View
114 lib/OrionRPCHooks.js
@@ -0,0 +1,114 @@
+/*
+
+OrionRPCHooks
+
+Sometimes you just need RPC stuff. You need some action to be taken on the server,
+like generating a PDF from a TeX file or something similar that would require loads of
+extra software to be installed by the client.
+
+This module allows you to define a set of functions as a node module and have them called
+using the RPC request.
+
+{ rpcRequest: { function:'', parameters: { set of parameters } } }
+
+In case policies have been defined, the RPC request will be first be presented to the
+policyModel and access can be denied.
+
+Your callback can have two parameters. The first parameter is the value to be given back,
+the second parameter is a boolean, denoting whether the value given back is a url.
+The idea behind it is that the result of the RPC call could be information that is not encodable
+in JSON, such as a binary file.
+In that case you want to be able to get the file using a single-use request.
+
+
+*/
+
+var sys = require('sys');
+var fs = require('fs');
+
+global.OrionRPCHooks = SC.Object.extend({
+
+ RPCHooksFile: null, // file name to load
+
+ _rpcHooks: null, // object to put the loaded file to
+
+ _rpcResult: null, // cache to save the information needed to return a specific request
+ // best to be an object:
+ // { 'cachekey': { mimeType: '', filePath: '' } }
+
+ callRPCFunction: function(functionName, params, callback){
+ if(!this._rpcHooks){
+ if(!this.RPCHooksFile) throw "No RPCHooksFile defined on RPCHooks Module";
+ else {
+ var rpc = require("." + this.RPCHooksFile);
+ if(!rpc) return NO;
+ else this._rpcHooks = rpc;
+ }
+ }
+ var func = this._rpcHooks[functionName],
+ me = this;
+ if(func){
+ func(params,function(result,isURL){
+ //{ mimeType: '', responseObject: '', filePath: '' }
+ var ret = { rpcResult: {} };
+ var mimeType = result.mimeType,
+ record = result.responseObject,
+ filePath = result.filePath;
+ if(mimeType === 'application/json'){
+ ret.rpcResult.record = record;
+ callback(ret);
+ }
+ else {
+ var cacheKey = me.generateCacheKey;
+ me._rpcCache[cacheKey] = { mimeType: mimeType, filePath: filePath };
+ ret.rpcResult.cacheKey = cacheKey;
+ callback(ret);
+ }
+ });
+ }
+ else {
+ callback({ rpcError: "Error"}); // don't let the message be too obvious...
+ }
+ },
+
+ rpcRetrieve: function(cacheKey,callback){
+ // function to return the file from the request
+ // it should also clean up the file after the response has been completed
+ // function should use the callback to notify the client
+ // syntax: callback(mimeType,data);
+ var rpcResponse = this._rpcResult[cacheKey];
+ var me = this; // needed by the clean up
+ if(rpcResponse && rpcResponse.filePath && rpcResponse.mimeType){
+ var filePath = rpcResponse.filePath, mimeType = rpcResponse.mimeType;
+ fs.readFile(filePath,function(err,data){
+ if(err) callback(null);
+ else {
+ callback(mimeType,data);
+ fs.unlink(filePath);
+ delete me._rpcCache[cacheKey];
+ }
+ });
+ }
+ else {
+ delete this._rpcResult[cacheKey]; // clean up
+ callback(null);
+ }
+ },
+
+ generateCacheKey: function(){
+ // the idea for this method was copied from the php site:
+ // http://www.php.net/manual/en/function.session-regenerate-id.php#60478
+ var keyLength = 32,
+ keySource = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ keySourceLength = keySource.length + 1, // we need to add one, to make sure the last character will be used in generating the key
+ ret = [],
+ curCharIndex = 0;
+
+ for(var i=0;i<=keyLength;i++){
+ curCharIndex = Math.floor(Math.random()*keySourceLength);
+ ret.push(keySource[curCharIndex]);
+ }
+ return ret.join('');
+ },
+
+});
View
59 lib/OrionServer.js
@@ -1,12 +1,7 @@
var http = require('http'),
url = require('url'),
fs = require('fs'),
- sys = require('sys'),
- send404 = function(res){
- res.writeHead(404);
- res.write('404');
- res.end();
- };
+ sys = require('sys');
if(!global.SC) require('./sc/runtime/core');
var querystring = require('querystring'); // for session key parsing
@@ -51,8 +46,16 @@ global.OrionServer = SC.Object.extend({
sessionModule: null,
+ rpcModule: null,
+
store: null, // place to bind the store / data source to
+ send404: function(res){
+ res.writeHead(404);
+ res.write('404');
+ res.end();
+ },
+
createHTTPHandler: function(serverObj){
var me = this;
return function(request, response){
@@ -70,9 +73,9 @@ global.OrionServer = SC.Object.extend({
response.write("request path: " + path + "<br>");
response.end();
}
- else {
- if(serverObj.forceAuth){
- var resource = path.slice(1);
+ else { // path is anything else as '/'
+ var resource = path.slice(1);
+ if(serverObj.forceAuth){
// make sure that the user is authenticated,
// but only after we found out the current request doesn't turn out to be an auth request
if(method === 'POST' && resource == 'auth'){ // force auth with posting
@@ -108,8 +111,16 @@ global.OrionServer = SC.Object.extend({
}
}
}
+ // handle all normal requests
switch(method){
- case 'GET': serverObj.GET(request,response); break;
+ case 'GET':
+ if(resource.indexOf('rpc'=== 0)){
+ serverObj.RPC(request,resource,response);
+ }
+ else {
+ serverObj.GET(request,response);
+ }
+ break;
case 'POST':
var postdata = "";
request.addListener("data", function(chunk){ // gather data
@@ -169,6 +180,27 @@ global.OrionServer = SC.Object.extend({
};
},
+ RPC: function(request, resource,response){
+ // RPC accepts a JSON object { rpcRequest: { cacheKey: ''}}
+ // call the RPC
+ // split the cacheKey from the resource
+ var resourceInfo = resource.split('/');
+ if(resourceInfo && (resourceInfo.length===2)){
+ var cacheKey = resourceInfo[1];
+ if(cacheKey && this.rpcModule){
+ var me = this;
+ this.rpcModule.rpcRetrieve(cacheKey, function(mimeType,data){
+ if(mimeType && data){
+ response.writeHead(200, {'Content-Type': mimeType });
+ response.write(data,'binary');
+ response.end();
+ }
+ else me.send404(); // force the end of the response if nothing can be found?
+ });
+ }
+ }
+ },
+
AUTH: function(request,data,response){
// when succesfully authenticated, send back a set-cookie header
// a standard PHP session start answers with the following headers on the auth request
@@ -198,7 +230,7 @@ global.OrionServer = SC.Object.extend({
var newCookieHeader = me.sessionModule.createSession(authResult);
response.writeHead(200, {'Content-Type': 'text/html', 'Set-Cookie':newCookieHeader });
var sessionInfoObj = querystring.parse(newCookieHeader,';','=');
- receivedSessionKey = sessionInfoObj[me.sessionModule.sessionName];
+ var receivedSessionKey = sessionInfoObj[me.sessionModule.sessionName];
response.write(JSON.stringify({sessionCookie: receivedSessionKey}));
}
else {
@@ -216,8 +248,11 @@ global.OrionServer = SC.Object.extend({
var me = this;
var path = url.parse(request.url).pathname;
var resource = path.slice(1); // return the entire string except the first character (being a "/")
+ response.writeHead(200, {'Content-Type': 'text/html'});
+ response.write('OrionServer: GET: received resource: ' + resource);
+ response.end();
// for the moment don't parse the resource, but just assume it is the model name
- this.store.fetch(resource,"student/1",this.createFetchCallback(request,response));
+ //this.store.fetch(resource,"student/1",this.createFetchCallback(request,response));
},
POST: function(request,data,response){
View
25 myRPCHooks.sample.js
@@ -0,0 +1,25 @@
+/*
+myRPCHooks sample
+
+make sure the callback is called with an object:
+
+{ mimeType: '', responseObject: '', filePath: '' }
+
+in case your mimeType is 'application/json', the server will return JSON stringified
+responseObject as the result of your RPC call
+
+in any other case, the server will assume you are sending a binary file.
+the responseJSON will be ignored, and the filePath will be retrieved from your response
+The server will then generate a single-use key the client can use to retrieve the
+result of the RPC action.
+So your function has to return the filePath where the server can find the file.
+
+*/
+
+var mimeTypeJSON = 'application/json';
+
+var sys = require('sys');
+
+exports.myFunc = function(params,callback){
+ callback({ mimeType: mimeTypeJSON, responseObject: {message: "This is a nice answer"} });
+};
2  riak-js
@@ -1 +1 @@
-Subproject commit 7f0f39812c08a2fad70aa69081d115a3a4f36c79
+Subproject commit 6882e18fb4bc78d6b7736d9d7d7442b8950b101d
Please sign in to comment.
Something went wrong with that request. Please try again.