postmanlabs / postman-app-support

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
https://www.postman.com
5.83k stars 838 forks source link

Pre-request Script: Unable to Retrieve GraphQL Request Body String #8495

Open ab-tools opened 4 years ago

ab-tools commented 4 years ago

Describe the bug Unable to retrieve GraphQL request body in pre-request script.

To Reproduce Steps to reproduce the behavior:

  1. Create a new POST query with a body type of GraphQL and add any simple query.
  2. Add console.log(pm.request.body.toString()); to the Pre-request Script section.
  3. Execute query and watch Postman Console. => Only Ø is shown as output.

Expected behavior The request body string - exactly as it will be sent to the server - get output to the Postman Console.

App information (please complete the following information):

Additional context It is important to receive the request body string - exactly as it will be sent to the server - before the request execution using the Pre-request Script to be able to generate e. g. a hash automatically for certain APIs. This also works fine when the request body type is just raw, but it seems not to be possible right now when the request body type is GraphQL.

kevinswiber commented 4 years ago

Hey @ab-tools,

You can log the GraphQL request body like so:

console.log(pm.request.body.graphql)

Hope that helps.

ab-tools commented 4 years ago

First thanks a lot for your quick reply, Kevin, much appreciated!

Unfortunately, that does not help - please see the Additional context part of my report: I need to get the exact body of the request as string to be able to calculate a hash of it (which is included in a header) before the request is sent for a certain API.

Do you know a way of how to get the exact request body for a GraphQL request?

I also tried it with JSON.stringify(pm.request.body.graphql) already, but this seems not to generate the same string that gets included in the request itself (it's a different hash value).

Thanks for any more ideas about that! Andreas

kevinswiber commented 4 years ago

@ab-tools Hmm, maybe something like this?

const body = pm.request.body.graphql;
const requestBody = `query: ${body.query}\nvariables: ${JSON.stringify(body.variables)}`
console.log(requestBody);
ab-tools commented 4 years ago

First thanks again for your support, Kevin!

I've directly tried what you suggested and added additionally the output of the body string length:

const body = pm.request.body.graphql;
const requestBody = `query: ${body.query}\nvariables: ${JSON.stringify(body.variables)}`
console.log(requestBody);
console.log(requestBody.length);

In my test the body length is logged as 461.

When I then check the actual post request in the console I see that it shows Content-Length: 458. So unfortunately this still does not match with what is actually sent to the server (if even already the length is different).

I think the main Postman code is closed source, right? Otherwise we could probably check there as I would expect that internally there also needs to be a rather simple method how Postman generates the actual request string based on the GraphQL query that gets sent to the server.

kevinswiber commented 4 years ago

This part is open source. You can check here for how it builds the request body: https://github.com/postmanlabs/postman-runtime/blob/d02d8f6d8c1f9c622258c9e0782c11924550ed7f/lib/requester/core-body-builder.js#L249-L302

kevinswiber commented 4 years ago

@ab-tools Stealing that code, here's what I got, and I think it matches. Give it a shot.

const content = pm.request.body.graphql;
const body = [];
const STRING = 'string'
const E = ''

if (content.hasOwnProperty('query') && (typeof content.query === STRING)) {
    body.push('"query":' + JSON.stringify(content.query));
}

if (content.hasOwnProperty('operationName') && (typeof content.operationName === STRING)) {
    body.push('"operationName":' + JSON.stringify(content.operationName));
}

if (content.hasOwnProperty('variables') && (typeof content.variables === STRING) &&
    (content.variables !== E)) {
    body.push('"variables":' + content.variables); // already a stringified JSON
}

const requestBody = '{' + body.join(',') + '}' // note that [] body = {}  ¯\_(ツ)_/¯
console.log(requestBody);
ab-tools commented 4 years ago

You're a genius, Kevin, can't say how much I appreciate you efforts here! And yes, that really matches, great! :-)

Although this works absolutely fine as a work-around for myself now, I still keep this issue open here as I would expect that exactly this string gets returned when you use pm.request.body.toString().

Additionally, I just noticed that the graphql request body type is completely missing in the documentation here, too: http://www.postmanlabs.com/postman-collection/RequestBody.html

So looks like somebody simply forgot to add this new request type both to the documentation as well as to the toString method. ;-)

Anyway, thanks again, Kevin! Andreas

ab-tools commented 3 years ago

Just hit the same issue once more and therefore checked this old ticket again: Although the work-around kindly provided by Kevin above still works it would really be great if this could be fixed to cleanup my pre-request scripts. ;-)

It looks like that the GraphQL request body mode was simply forgotten to be handled in the body.toString method here: http://www.postmanlabs.com/postman-collection/collection_request-body.js.html#line164