MartinSahlen/cloud-functions-python

TypeError: req.set is not a function

santhoshdc1590 opened this issue · 6 comments

I used the function.py 's handle_http as my cloud function to be built

def handle_http(req):
    
    biquery_client = bigquery.Client(credentials=get_credentials())
    
    if req.method == 'OPTIONS':
        return JsonResponse({"status":"success"},status=200,safe=False)
    
    if req.method == 'GET':
        print("Body: ",type(req.GET.getlist('image')))
        print(type(req.GET.getlist('image')),req.GET.getlist('image'))

        i=req.GET.getlist('image')
        i=''.join(str(e) for e in i)

        print(i)

        rID = req.GET.getlist('refID')
        rTable = req.GET.getlist('refTable')
        print(rID," ",rTable)
        ########### main custom function. which I am calling ##################
        mainP(i,rID,rTable)

        return Response(
            status_code=200,
            body={'key': 2},
            headers={'content-type': 'application/json'},
        )


handle_http_event(handle_http)

This was the index.js generated after the build below

var googleAuth = require('google-auto-auth')();
//Handle Background events according to spec
function shimHandler(data) {
  return new Promise((resolve, reject) => {
    googleAuth.getToken(function (err, oauthToken) {
      if (err) {
        reject()
      } else {
        const p = require('child_process').execFile('./dist/func/func', {
          env: Object.assign(process.env, {
            'GOOGLE_OAUTH_TOKEN': oauthToken,
          })
        });
        var lastMessage;
        p.stdin.setEncoding('utf-8');
        //Log standard err messages to standard err
        p.stderr.on('data', (err) => {
          console.error(err.toString());
        })
        p.stdout.on('data', (out) => {
          console.log(out.toString());
          lastMessage = out;
        })
        p.on('close', (code) => {
          if (code !== 0) {
            //This means the shim failed / panicked. So we reject hard.
            reject();
          } else {
            // Resolve the promise with the latest output from stdout
            // In case of shimming http, this is the response object.
            resolve(lastMessage);
          }
        });
        //Write the object/message/request to the shim's stdin and signal
        //End of input.
        p.stdin.write(JSON.stringify(data));
        p.stdin.end();
      }
    });
  });
}

//Handle http request
function handleHttp(req, res) {
  var requestBody;
  switch (req.get('content-type')) {
    case 'application/json':
      requestBody = JSON.stringify(req.body);
      break;
    case 'application/x-www-form-urlencoded':
      //The body parser for cloud functions does this, so just play along
      //with it, sorry man! Maybe we should construct some kind of proper
      //form request body? or not. let's keep it this way for now, as
      //This is how cloud functions behaves.
      req.set('content-type', 'application/json')
      requestBody = JSON.stringify(req.body);
      break;
    case 'application/octet-stream':
      requestBody = req.body;
      break;
    case 'text/plain':
      requestBody = req.body;
      break;
  }

  var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;

  var httpRequest = {
    'body': requestBody,
    'headers': req.headers,
    'method': req.method,
    'remote_addr': req.ip,
    'url': fullUrl
  };

  shimHandler(httpRequest)
  .then((result) => {
    data = JSON.parse(result);
    res.status(data.status_code);
    res.set(data.headers)
    res.send(data.body);
  })
  .catch(() => {
    res.status(500).end();
  })
}

//
exports['handle_http'] = function(req, res) {
  return handleHttp(req, res);
}//

When testing the url generated locally using POSTMAN

I'm getting this error

{"stack":"TypeError: req.set is not a function\n at handleHttp (/Users/santhoshdc/Documents/LaunchTestOne/cloudfn/target/index.js:55:11)\n at exports.handle_http (/Users/santhoshdc/Documents/LaunchTestOne/cloudfn/target/index.js:90:10)\n at app.use (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/src/supervisor/worker.js:142:11)\n at Layer.handle [as handle_request] (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/node_modules/express/lib/router/index.js:317:13)\n at /Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/node_modules/express/lib/router/index.js:284:7\n at Function.process_params (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/node_modules/express/lib/router/index.js:335:12)\n at next (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/node_modules/express/lib/router/index.js:275:10)\n at app.use (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/src/supervisor/worker.js:114:7)\n at Layer.handle [as handle_request] (/Users/santhoshdc/.nvm/versions/node/v9.8.0/lib/node_modules/@google-cloud/functions-emulator/node_modules/express/lib/router/layer.js:95:5)","message":"req.set is not a function","name":"TypeError"}

@MartinSahlen or anyone else who knows what's going on here, help please?

What is going on is that this line: https://github.com/MartinSahlen/cloud-functions-python/blob/master/cloudfn/template/index.js#L55 tries to call the function set which does not seem exist on the request object and thereby fails. Likely because noone so far has tried using a cloud function for posting form data - or it existed in a previous version of express and you seem to be using the emulator. It should be an easy fix to go around this bug - feel free to submit a pull request to fix this @santhoshdc1590 👍

I'm new to this web thing. I do understand that the req.set doesn't exist for the program to call it. I'm unable to find an alternative for req.set. Yes I'm using emulator. express is new to me again. Would really appreciate if you could guide me through this 😅

Unfortunately I do not have a lot of time to provide guidance but I can try to fix the bug when I can carve out some time.

@MartinSahlen I couldn't find any function called req.set in express2.x or express3.x can I use req.accepts('application/json') instead to req.set('content-type', 'application/json') from express4.x?

I think it can be just removed, is it is an error on my end as I said. Since JavaScript is not statically typed this error is not caught and occurs at runtime when posting data using forms.

I commented the req.set, built and installed cloud function python again.

Built and deployed the function similarly locally.

Now I am not getting 500 internal server error on POSTMAN and black screen in browser

I tried changing https://github.com/MartinSahlen/cloud-functions-python/blob/master/cloudfn/template/index.js#L55 and checked what's happening

This is the changed program

var googleAuth = require('google-auto-auth')();
//Handle Background events according to spec
function shimHandler(data) {
  return new Promise((resolve, reject) => {
    googleAuth.getToken(function (err, oauthToken) {
      if (err) {
        reject()
      } else {
        const p = require('child_process').execFile('./dist/{{config["output_name"]}}/{{config["output_name"]}}', {
          env: Object.assign(process.env, {
            'GOOGLE_OAUTH_TOKEN': oauthToken,
          })
        });
        var lastMessage;
        p.stdin.setEncoding('utf-8');
        //Log standard err messages to standard err
        p.stderr.on('data', (err) => {
          console.error(err.toString());
        })
        p.stdout.on('data', (out) => {
          console.log(out.toString());
          lastMessage = out;
        })
        p.on('close', (code) => {
          if (code !== 0) {
            //This means the shim failed / panicked. So we reject hard.
            reject();
          } else {
            // Resolve the promise with the latest output from stdout
            // In case of shimming http, this is the response object.
            resolve(lastMessage);
          }
        });
        //Write the object/message/request to the shim's stdin and signal
        //End of input.
        p.stdin.write(JSON.stringify(data));
        p.stdin.end();
      }
    });
  });
}

//Handle http request
function handleHttp(req, res) {
  var requestBody;
  console.log('hello')
  console.log(req.get('content-type'))
  switch (req.get('content-type')) {
    case 'application/json':
      requestBody = JSON.stringify(req.body);
      break;
    case 'application/x-www-form-urlencoded': //application/x-www-form-urlencoded
      //The body parser for cloud functions does this, so just play along
      //with it, sorry man! Maybe we should construct some kind of proper
      //form request body? or not. let's keep it this way for now, as
      //This is how cloud functions behaves.
      //req.setHeader('content-type', 'application/json')

      requestBody = JSON.stringify(req.body);
      console.log('this should be the case, the result of switch statement');
      requestBody = JSON.stringify(req.body);
      console.log('Request body is: ');
      console.log(requestBody);
      break;
    case 'application/octet-stream':
      requestBody = req.body;
      break;
    case 'text/plain':
      requestBody = req.body;
      break;
  }

  var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;

  var httpRequest = {
    'body': requestBody,
    'headers': req.headers,
    'method': req.method,
    'remote_addr': req.ip,
    'url': fullUrl
  };

  shimHandler(httpRequest)
  .then((result) => {
    console.log('should come here')
    data = JSON.parse(result);
    res.status(data.status_code);
    res.set(data.headers)
    res.send(data.body);
  })
  .catch((e) => {
    console.log(e);
    console.log('some error has occurred');
    res.status(500).end();
  })
}

//{% if config["trigger_http"] %}
exports['{{config["function_name"]}}'] = function(req, res) {
  return handleHttp(req, res);
}//{% else %}
exports['{{config["function_name"]}}'] = function(event, callback) {
  return shimHandler(event.data).then(function() {
    callback();
  }).catch(function() {
    callback(new Error("Function failed"));
  });
}//{% endif %}

This is what I get on running logs using functions logs read

2018-03-23T13:37:16.389Z - info: User function triggered, starting execution
2018-03-23T13:37:16.389Z - info: hello
application/x-www-form-urlencoded
this should be the case, the result of switch statement
Request body is:
{}
2018-03-23T13:37:16.410Z - info: undefined
2018-03-23T13:37:16.411Z - info: some error has occurred
2018-03-23T13:37:16.415Z - info: Execution took 29 ms, user function completed successfully