0

I have a Promise that used to wait for a full JSON string to be received, but now it just returns only a fraction of the JSON string. I am currently using the Shopify API to create customers and this is what the response string is supposed to look like:

{
  "customer": {
    "id": 1073339461,
    "email": "steve.lastnameson@example.com",
    "accepts_marketing": false,
    "created_at": "2019-04-18T15:42:33-04:00",
    "updated_at": "2019-04-18T15:42:33-04:00",
    "first_name": "Steve",
    "last_name": "Lastnameson",
    "orders_count": 0,
    "state": "disabled",
    "total_spent": "0.00",
    "last_order_id": null,
    "note": null,
    "verified_email": true,
    "multipass_identifier": null,
    "tax_exempt": false,
    "phone": "+15142546011",
    "tags": "",
    "last_order_name": null,
    "currency": "USD",
    "addresses": [
      {
        "id": 1053317291,
        "customer_id": 1073339461,
        "first_name": "Mother",
        "last_name": "Lastnameson",
        "company": null,
        "address1": "123 Oak St",
        "address2": null,
        "city": "Ottawa",
        "province": "Ontario",
        "country": "Canada",
        "zip": "123 ABC",
        "phone": "555-1212",
        "name": "Mother Lastnameson",
        "province_code": "ON",
        "country_code": "CA",
        "country_name": "Canada",
        "default": true
      }
    ],
    "accepts_marketing_updated_at": "2019-04-18T15:42:33-04:00",
    "marketing_opt_in_level": null,
    "admin_graphql_api_id": "gid://shopify/Customer/1073339461",
    "default_address": {
      "id": 1053317291,
      "customer_id": 1073339461,
      "first_name": "Mother",
      "last_name": "Lastnameson",
      "company": null,
      "address1": "123 Oak St",
      "address2": null,
      "city": "Ottawa",
      "province": "Ontario",
      "country": "Canada",
      "zip": "123 ABC",
      "phone": "555-1212",
      "name": "Mother Lastnameson",
      "province_code": "ON",
      "country_code": "CA",
      "country_name": "Canada",
      "default": true
    }
  }
}

However, this is what I am actually getting back:

{"customer

The Promise I have is in a function that is supposed to wait for the result before continuing, but as you can see, it's severely cutting off the string for some reason.

For the past 6 months from when I first wrote the function, it has worked without issue. Today is the first day it started doing this.

Here is the function (Node 8.10 on AWS Lambda):

async function makeCall(path, method, data = '', again = true) {

    return new Promise((resolve, reject) => {
        const bearer = "Basic " + Buffer.from(shopKey + ":" + shopPass).toString('base64');
        const options = {
            host: shopURL,
            path: path,
            method: method,
            headers: {
                "Content-Type" : "application/json",
                "Authorization" : bearer
            }
        };


        const req = http.request(options, (res) => {


        });

        req.on('response', function(res){

            res.on('data', function(chunk){
                const body  = chunk.toString('utf8');
                console.log('chunk', chunk);
                console.log(body);
                resolve(JSON.parse(body));
            });

        });

        req.on('error', (e) => {
            if (again){
                setTimeout(makeCall(path, method, data, again = false), 3000);
            } else {
                reject(e.message);
            }

        });

        // send the request
        req.write(JSON.stringify(data));
        req.end();
    });

}

And this is the function I have that is calling the above function:

const customer = await makeCall(path, method, data);
Eric Brown
  • 1,312
  • 3
  • 15
  • 30
  • If you get an error your retry function won't work, as it will execute immediately, you can't pass named variables, and it wont handle the promise result. It should look more like `setTimeout(() => makeCall(path, method, data, false).then(resolve).catch(reject), 3000);` – braza May 09 '19 at 22:56
  • Thanks, I made the adjustment, but it doesn't help my main issue which is that the response string is being cut off. – Eric Brown May 09 '19 at 23:01
  • You should be waiting for the `end` event before resolving. See answer [here](https://stackoverflow.com/questions/19539391/how-to-get-data-out-of-a-node-js-http-get-request) – Mark May 09 '19 at 23:09
  • @EricBrown I've posted an answer with the fix I mentioned above as well as what should fix the main issue as well. – braza May 09 '19 at 23:12

1 Answers1

1

I think the issue is the way you are doing chunking. It's possible for the request to send you data piece by piece, so you need to change your code to:

async function makeCall(path, method, data = '', again = true) {
  return new Promise((resolve, reject) => {
    const bearer = "Basic " + Buffer.from(shopKey + ":" + shopPass).toString('base64');
    const options = {
      host: shopURL,
      path: path,
      method: method,
      headers: {
        "Content-Type" : "application/json",
        "Authorization" : bearer
      }
    };

    let body = '';
    const req = http.request(options, (res) => {
      // Here we are potentially getting chunks of data that we are
      // adding to the main result "body"
      res.on('data', function(chunk){
        body += chunk;
      });
      // Once the request ends we can resolve with the result
      res.on('end', function() {
        resolve(JSON.parse(body));
      });
    });

    req.on('error', (e) => {
      if (again){
        // Updated so that it forwards on the response/error,
        // and only gets called after the timeout
        setTimeout(() => makeCall(path, method, data, false).then(resolve).catch(reject), 3000);
      } else {
        reject(e.message);
      }
    });

    // send the request
    req.write(JSON.stringify(data));
    req.end();
  });
};
braza
  • 4,260
  • 1
  • 26
  • 36
  • You may also need to call `toString('utf8')` on the `chunk` or the `body` – braza May 09 '19 at 23:21
  • This mostly works, but I am now also getting the string 'undefined' before the JSON I need to parse and that's now what's causing issues. Do you know a way around that, or should I strip that out with regex? – Eric Brown May 09 '19 at 23:25
  • I updated the answer a few minutes ago with `let body = ''` That is probably what you need to change if you haven't already? – braza May 09 '19 at 23:27