MartinSahlen/cloud-functions-python

New to this, need some help!

Closed this issue · 15 comments

I followed all the steps to setup all the requirements for this cloud-function-python

I have a python file that I would like to execute. I have all the virtual environment libraries those are required in a requirements.txt file.

Can anyone guide me what should I do to execute the python file?

Please have a look at the examples, they should suffice to give you an understanding of how to execute your own code. for instance, https://github.com/MartinSahlen/cloud-functions-python/blob/master/examples/http/function.py should suffice for a simple case.

@MartinSahlen

from cloudfn.http import handle_http_event, Response


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


handle_http_event(handle_http)

firstly:

I need to check if req in def handle_http(req): is in the pre defined(desired) format.

Depending on the req 's content, change the return response .

If the req format satisfies the pre defined(desired) format. Execute the main code that is to be executed in the function named handle_http send the data through the body in the return response

Am I correct here?

I'm sorry, I don't really understand what you are trying to achieve here. The request object is defineded here https://github.com/MartinSahlen/cloud-functions-python/blob/master/cloudfn/http.py, you can use all available fields. I don't understand what the "pre-defined" format means.

Sorry my bad, Thank you for the link on how the req is structured here.

In simple words, I can have other python functions just in this functions.py similar to handle_http and call those functions to manipulate the data inside function handle_http(req). Will this work? I think this show work.

from cloudfn.http import handle_http_event, Response


def function1():
    #some operation
    return value

def function2():
   #some operation
    return value

def handle_http(req):
    #call function1
    
    #call function2

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


handle_http_event(handle_http)

Any function that returns a Response (https://github.com/MartinSahlen/cloud-functions-python/blob/master/cloudfn/http.py#L27) object will work. What that function does before returning the Response object is obviously up to you, the developer, to decide and implement.

@MartinSahlen

from cloudfn.http import handle_http_event, Response


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


handle_http_event(handle_http)

Correct me if I'm wrong req parameter in the function handle_http will actually contain the url of the cloud-function along with any parameters sent by the client calling the cloud function url?

Example
req='http://cloudfunctionurl?data=rawdata'

The req paramater is of the type https://github.com/MartinSahlen/cloud-functions-python/blob/master/cloudfn/http.py as already mentioned. If you hit a cloud function with http://cloudfunctionurl?data=rawdata, where data is a query parameter, then you can find it by going to req.query. However I suspect you may be looking to POST data, then the body of the http request will be found in req.body.

Nice! This basically reduces coding and saves time by not having to create an API to hit the implemented cloud function?

I don't get what you mean @santhoshdc1590. It would be great if you could provide some more examples, code and explanations of what you are trying to achieve here.

Example:

  • I have a python code.
  • I need this python code to be executed by the client by feeding the python code with the required data parameters.

Generally we write an API so that the client can hit it along with the required parameters and then return the response along with the data client asked for.

Executing this example python code through cloud function python wouldn't require the developer to write an api for the client to hit, as an URL to hit the cloud function python is provided already after this is deployed on cloud?
Am I correct?

You are absolutely correct :D The point of cloud functions is to be able to take a piece of code and make it trigger on events in the cloud, such as an http request. This said piece of code is your custom code that you want to run in response to such an event. When you are deploying your function to trigger on http events, it will automatically create a URL for you to direct your http client to 👍 I would recommend reading up on the docs here to get some more understanding: https://cloud.google.com/functions/ (it is not documented in python as this is just a shim but the semantics stilly apply).

The URL of your deployed function is described here: https://cloud.google.com/functions/docs/calling/http

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"}

I am trying to decode this error can you point where I went wrong?

@MartinSahlen Can you help me with this? I don't know what's happening here

Thank You @MartinSahlen I guess you can't read this. I will be opening a new issues for this