Search code examples
javascriptropencpu

Call a R function in OpenCPU with file arguments


I want to let a user call a custom R function with custom arguments, using a web-based interface. Uploading local files for use should be possible.

I tried to implement this using OpenCPU which worked fine until now. The user's code is packed into a ocpu.Snippet and passed together with the arguments args into

ocpu.call(
    'do.call',
    {
        'what': snippet,
        'args': args
    },
    session => /* processing of return value and console output */
);

This works fine for all argument types I've worked with so far.

Now that I'm testing the upload of files, I have found out the following:

  1. The JavaScript wrapper (forked to remove annoying alerts) only detects files if they are on the upmost level (like snippet and args in the example above). This means that since my args contain a file object (args = { file: File }) they are not detected when using the call above. However, calling ocpu.call('read.csv', args, ...) works, since now the wrapper detects file: File.
  2. OpenCPU only allows file uploads using multipart/form-data. As far as I have learned, a nested file as I have in my original call is not representable in this format.
  3. Instead of using do.call I could write a custom R function to use, which allows for some way of providing used files as "first-order" arguments and then somehow passes them to the provided function.

So I'm stuck, since I don't know how to write such a function and I really wonder if there is better way to call a custom function and control its arguments remotely. Do you have any idea how to solve this?


Solution

  • After fiddling around a bit, I found that the function was relatively easy to write:

    functionCall <- function(func, ...) {
      return(do.call(func, list(...)))
    }
    

    After making that function available in OpenCPU, it can be called using

    args = {
      func: snippet,
      x: firstArg,
      y: secondArg,
      // arbitrarily many args
    }
    
    opencpu.call(
      'functionCall',
      openCpuArgs,
      callback
    );
    

    I tested this with a snippet that calls read.csv and it successfully printed its content instead of returning an error.