Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

createTask requires body to be a buffer #1301

Open
ebidel opened this issue Oct 6, 2018 · 14 comments
Open

createTask requires body to be a buffer #1301

ebidel opened this issue Oct 6, 2018 · 14 comments
Assignees
Labels
api: cloudtasks Issues related to the Cloud Tasks API. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@ebidel
Copy link

ebidel commented Oct 6, 2018

Environment details

  • OS: App Engine Standard Node JS
  • Node.js version: 8
  • npm version:
  • @google-cloud/tasks version: 0.22.x

Steps to reproduce

The following code produces an "invalid encoding" error within the SDK:

  const url = 'https://example.com';

   const client = new cloudTasks.CloudTasksClient();
    const response = await client.createTask({
      parent: client.queuePath(PROJECT_ID, LOCATION, QUEUE),
      task: {
        appEngineHttpRequest: {
          httpMethod: 'POST',
          relativeUri: '/audit',
          body: JSON.stringify({url}),
          headers: {'Content-Type': 'application/json'},
        },
      },
    });

server.js:

...
app.use(bodyParser.json());

app.post('/audit', async (req, resp, next) => {
  let url = req.body.url; // undefined
....
});
@JustinBeckwith JustinBeckwith added the triage me I really want to be triaged. label Oct 6, 2018
@stijnvn
Copy link

stijnvn commented Oct 7, 2018

I can confirm this issue.

When you leave out the JSON.stringify() call when creating the body, you don't get an invalid encoding error, but the response body will be undefined. When this has been fixed, will the explicit conversion to string still be necessary?

@JustinBeckwith JustinBeckwith added the 🚨 This issue needs some love. label Oct 11, 2018
@ebidel
Copy link
Author

ebidel commented Nov 3, 2018

Any update on this?

When this has been fixed, will the explicit conversion to string still be necessary?

Not sure about the internals of what actually makes the request, but other APIs like fetcha nd xhr require you to JSON.stringify the body if it's json.

@stephencaldwell
Copy link

The documentation is either incorrect or the wrapper code is, but the body property of the task object needs to be a Buffer or a base64 encoded string.

{
   ...
   body: Buffer.from(JSON.stringify(payload)),
   ...
}

or

{
   ...
   body: Buffer.from(JSON.stringify(payload)).toString('base64'),
   ...
}

@stephencaldwell
Copy link

JSON.stringify({url}) is valid.

It's a shorthand syntax for creating object properties that have the same name as the variable.

eg:

const x = "abc";
const y = {x};

console.log(y); // produces { x: 'abc' }
console.log(JSON.stringify({x})); //produces '{"x": "abc"}'

@nikmartin
Copy link

Also, just FYI I'm encoding the entire JSON body and NOT setting the headers and it's working fine:

In my task creator:

const task = {
      appEngineHttpRequest: {
        httpMethod: 'POST',
        relativeUri: '/tasks/import',
        body: Buffer.from(JSON.stringify({ storeid: storeID, page: page })),
      },
    };

In my task handler:

const paramsObj = JSON.parse(Buffer.from(req.body, 'base64').toString('utf-8'));

I think the docs are wrong about being able to send JSON bodies, I think all payloads have to be base64 encoded strings.

@JustinBeckwith JustinBeckwith added type: question Request for information or clarification. Not an issue. and removed 🚨 This issue needs some love. triage me I really want to be triaged. labels Nov 28, 2018
@dinigo
Copy link

dinigo commented Nov 29, 2018

What you can do is tell the receiver that the information they receive is in JSON so that, for example, express parses it automagically. This is generalized but extracted from working code

const {CloudTasksClient} = require('@google-cloud/tasks');
const client = new CloudTasksClient();

// content of the task
const payload = {
  foo: 'bar',
};

// configuration for the task
const request = {
  parent: client.queuePath('my-project', 'my-location', 'my-batch'),
  task: {
    appEngineHttpRequest: {
      httpMethod: 'POST',
      relativeUri: '/endpoint',
      body: Buffer.from(JSON.stringify(payload)).toString('base64'),
      headers: {
        'Content-Type': 'application/json',
      },
    },
  },
};
return client.createTask(request);

And the handler would be...

app.post('/endpoint', (req, res) => {
  const {foo} = req.body;
  console.log(foo); // "bar"
  res.send('ok');
});

@JustinBeckwith JustinBeckwith added type: docs Improvement to the documentation for an API. and removed type: question Request for information or clarification. Not an issue. labels Dec 9, 2018
@JustinBeckwith
Copy link
Contributor

JustinBeckwith commented Dec 9, 2018

@alexander-fenster To give you the TL;DR of the thread, when you create a new task, it expects you to pass a buffer instead of an object or a string. So instead of this:

appEngineHttpRequest: {
  httpMethod: 'POST',
  relativeUri: '/audit',
  body: JSON.stringify({ url }),
  headers: { 'Content-Type': 'application/json' },
},

You have to Buffer.from the object in question:

appEngineHttpRequest: {
  httpMethod: 'POST',
  relativeUri: '/audit',
  body: Buffer.from(JSON.stringify({ url })),
  headers: { 'Content-Type': 'application/json' },
},

I would have never figured this out if not for the create task sample 🤷‍♂️ . Is there a way we could have the generated API here accept both an object and a buffer?

@JustinBeckwith JustinBeckwith changed the title createTask does not accept POST body of application/json createTask requires body to be a buffer Dec 9, 2018
@dinigo
Copy link

dinigo commented Dec 10, 2018

@JustinBeckwith

I would have never figured this out if not for the create task sample

Same here. There's where I got the idea. I also remembered that PubSub body in cloud functions was a buffer too.

I agree it would be nice to have the data serialized and deserialized by the library itself.

@askdhyan
Copy link

What you can do is tell the receiver that the information they receive is in JSON so that, for example, express parses it automagically. This is generalized but extracted from working code

const {CloudTasksClient} = require('@google-cloud/tasks');
const client = new CloudTasksClient();

// content of the task
const payload = {
  foo: 'bar',
};

// configuration for the task
const request = {
  parent: client.queuePath('my-project', 'my-location', 'my-batch'),
  task: {
    appEngineHttpRequest: {
      httpMethod: 'POST',
      relativeUri: '/endpoint',
      body: Buffer.from(JSON.stringify(payload)).toString('base64'),
      headers: {
        'Content-Type': 'application/json',
      },
    },
  },
};
return client.createTask(request);

And the handler would be...

app.post('/endpoint', (req, res) => {
  const {foo} = req.body;
  console.log(foo); // "bar"
  res.send('ok');
});

Really Works! :)

@bcoe bcoe added type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. and removed type: docs Improvement to the documentation for an API. labels Dec 2, 2019
@bcoe
Copy link
Contributor

bcoe commented Dec 2, 2019

👋 in the documentation in samples we already document the Buffer behavior; @alexander-fenster I think what shakes out of this thread is a feature request for a string body to be accepted, along with a Buffer.

@google-cloud-label-sync google-cloud-label-sync bot added the api: cloudtasks Issues related to the Cloud Tasks API. label Jan 30, 2020
@averikitsch
Copy link

@alexander-fenster Do you know if this is specific to this library or the API?

@flo-sch
Copy link

flo-sch commented Mar 24, 2021

I ended up here as well quite confused, while using a Cloud Run container as HTTP target.

I found the following unclear:

  1. that the payload passed to createTask() needs to be sent as a buffer (it is indeed shown in the examples, but not obvious whether this is an actual requirement, or just handled this way in the examples)
  2. that, even though you need to "Bufferize" it, you do not need to parse it from the target handler (which I discovered here, thanks to the request headers)
    (I really thought the target handler would need to handle an application/octet-stream body with base64 encoded payload)

IMO specifying both of those in the documentation would be beneficial :)

@summer-ji-eng
Copy link
Collaborator

summer-ji-eng commented Apr 5, 2022

👋 in the documentation in samples we already document the Buffer behavior; @alexander-fenster I think what shakes out of this thread is a feature request for a string body to be accepted, along with a Buffer.

It is great to have an example code in sample. As a external user, it is so hard to figure out the use a base 64 string. In the sample code we generated, we only initialize the variable in empty object: const task = {} (code). Maybe @bcoe @sofisl. In generator, we have an sample code for reference. It construct a request with field value. It should help (code).

@sofisl sofisl transferred this issue from googleapis/nodejs-tasks Nov 11, 2022
@arvin1408
Copy link

It really should be mentioned somewhere at least in the documentation (https://cloud.google.com/walkthroughs/cloudtasks/cloudtasks-nodejs) that the base64 formatted string Buffer is a requirement. The "serialization error" were generic, not specific to the body element, thus not helpful in pointing to the body formatting problem. After getting stuck, it's only with luck that one lands on this external thread explaining both the issue and solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: cloudtasks Issues related to the Cloud Tasks API. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests