{"action":"created","issue":{"url":"https://api.github.com/repos/paypal/PayPal-node-SDK/issues/69","labels_url":"https://api.github.com/repos/paypal/PayPal-node-SDK/issues/69/labels{/name}","comments_url":"https://api.github.com/repos/paypal/PayPal-node-SDK/issues/69/comments","events_url":"https://api.github.com/repos/paypal/PayPal-node-SDK/issues/69/events","html_url":"https://github.com/paypal/PayPal-node-SDK/issues/69","id":57598803,"number":69,"title":"`utils.merge` has a bug that is causing some other failures.","user":{"login":"kobalicek","id":6491301,"avatar_url":"https://avatars.githubusercontent.com/u/6491301?v=3","gravatar_id":"","url":"https://api.github.com/users/kobalicek","html_url":"https://github.com/kobalicek","followers_url":"https://api.github.com/users/kobalicek/followers","following_url":"https://api.github.com/users/kobalicek/following{/other_user}","gists_url":"https://api.github.com/users/kobalicek/gists{/gist_id}","starred_url":"https://api.github.com/users/kobalicek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kobalicek/subscriptions","organizations_url":"https://api.github.com/users/kobalicek/orgs","repos_url":"https://api.github.com/users/kobalicek/repos","events_url":"https://api.github.com/users/kobalicek/events{/privacy}","received_events_url":"https://api.github.com/users/kobalicek/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/paypal/PayPal-node-SDK/labels/bug","name":"bug","color":"fc2929"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":7,"created_at":"2015-02-13T13:32:02Z","updated_at":"2015-02-19T11:37:57Z","closed_at":null,"body":"Guys, I spent today some time with a bug on your side that is happening in `api.js`, this code:\r\n\r\n```\r\nfunction updateToken(http_options, error_callback, callback) {\r\n    generateToken(http_options, function (error, token) {\r\n        if (error) {\r\n            error_callback(error, token);\r\n        } else {\r\n            http_options.headers.Authorization = token;\r\n            callback();\r\n        }\r\n    });\r\n}\r\n```\r\n\r\nThe problem is that `http_option.headers` is undefined so it throws TypeError. I tracked it down and the issue is most probably in your `utils.merge` function, that uses some weird approach to merge properties from one object to another, and it fails in certain cases.\r\n\r\nI wrote a test program to verify your `utils.merge`, If you install paypal-rest-sdk locally, you should be able to reproduce it:\r\n\r\n```\r\nvar utils = require(\"./node_modules/paypal-rest-sdk/lib/utils\");\r\n\r\nvar obj1 = {\r\n  \"host\": \"api.sandbox.paypal.com\",\r\n  \"port\": \"\",\r\n  \"client_id\": \"SomeId\",\r\n  \"client_secret\": \"SomeSecret\"\r\n};\r\n\r\nvar obj2 = {\r\n  \"mode\": \"sandbox\",\r\n  \"schema\": \"https\",\r\n  \"host\": \"api.sandbox.paypal.com\",\r\n  \"port\": \"\",\r\n  \"openid_connect_schema\": \"https\",\r\n  \"openid_connect_host\": \"api.sandbox.paypal.com\",\r\n  \"openid_connect_port\": \"\",\r\n  \"authorize_url\": \"https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize\",\r\n  \"logout_url\": \"https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/endsession\",\r\n  \"headers\": {}\r\n};\r\n\r\nconsole.log(\"OBJ1 = \" + JSON.stringify(obj1, null, 2));\r\nconsole.log(\"OBJ2 = \" + JSON.stringify(obj2, null, 2));\r\n\r\nvar result = utils.merge(obj1, obj2);\r\nconsole.log(\"RESULT = \" + JSON.stringify(result, null, 2));\r\n```\r\n\r\nThe console output is:\r\n\r\n```\r\nOBJ1 = {\r\n  \"host\": \"api.sandbox.paypal.com\",\r\n  \"port\": \"\",\r\n  \"client_id\": \"SomeId\",\r\n  \"client_secret\": \"SomeSecret\"\r\n}\r\nOBJ2 = {\r\n  \"mode\": \"sandbox\",\r\n  \"schema\": \"https\",\r\n  \"host\": \"api.sandbox.paypal.com\",\r\n  \"port\": \"\",\r\n  \"openid_connect_schema\": \"https\",\r\n  \"openid_connect_host\": \"api.sandbox.paypal.com\",\r\n  \"openid_connect_port\": \"\",\r\n  \"authorize_url\": \"https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize\",\r\n  \"logout_url\": \"https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/endsession\",\r\n  \"headers\": {}\r\n}\r\nRESULT = {\r\n  \"host\": \"api.sandbox.paypal.com\",\r\n  \"port\": \"\",\r\n  \"client_id\": \"SomeId\",\r\n  \"client_secret\": \"SomeSecret\",\r\n  \"mode\": \"sandbox\",\r\n  \"schema\": \"https\",\r\n  \"openid_connect_schema\": \"https\",\r\n  \"openid_connect_host\": \"api.sandbox.paypal.com\",\r\n  \"openid_connect_port\": \"\",\r\n  \"authorize_url\": \"https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize\",\r\n  \"logout_url\": \"https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/endsession\"\r\n}\r\n```\r\n\r\nThe `headers` object is NOT in the result, but your code in api.js assumes it's there."},"comment":{"url":"https://api.github.com/repos/paypal/PayPal-node-SDK/issues/comments/75038198","html_url":"https://github.com/paypal/PayPal-node-SDK/issues/69#issuecomment-75038198","issue_url":"https://api.github.com/repos/paypal/PayPal-node-SDK/issues/69","id":75038198,"user":{"login":"kobalicek","id":6491301,"avatar_url":"https://avatars.githubusercontent.com/u/6491301?v=3","gravatar_id":"","url":"https://api.github.com/users/kobalicek","html_url":"https://github.com/kobalicek","followers_url":"https://api.github.com/users/kobalicek/followers","following_url":"https://api.github.com/users/kobalicek/following{/other_user}","gists_url":"https://api.github.com/users/kobalicek/gists{/gist_id}","starred_url":"https://api.github.com/users/kobalicek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kobalicek/subscriptions","organizations_url":"https://api.github.com/users/kobalicek/orgs","repos_url":"https://api.github.com/users/kobalicek/repos","events_url":"https://api.github.com/users/kobalicek/events{/privacy}","received_events_url":"https://api.github.com/users/kobalicek/received_events","type":"User","site_admin":false},"created_at":"2015-02-19T11:37:57Z","updated_at":"2015-02-19T11:37:57Z","body":"I experimented with a merge implementation that probably does what you want, just take a look if you are interested. Some cases are missing as I'm not sure how you intent to handle incompatible types, like `a` is `[]` and `b` is `{}`. I think the function should check for these conditions and fail in such cases.\r\n\r\n```JS\r\nvar isArray = Array.isArray;\r\nvar hasOwn = Object.prototype.hasOwnProperty;\r\n\r\nfunction clone(v) {\r\n  if (v == null || typeof v !== \"object\")\r\n    return v;\r\n\r\n  if (isArray(v)) {\r\n    var arr = v.slice();\r\n    for (var i = 0; i < srcArr.length; i++)\r\n      arr[i] = clone(arr[i]);\r\n    return arr;\r\n  }\r\n  else {\r\n    var obj = {};\r\n    for (var k in v)\r\n      obj[k] = clone(v[k]);\r\n    return obj;\r\n  }\r\n}\r\n\r\nfunction merge(a, b) {\r\n  // Handle invalid arguments.\r\n  if (a == null || typeof a !== \"object\")\r\n    throw new TypeError(\"merge() - 'a' has to be an object, not \" + typeof a + \".\");\r\n\r\n  if (b == null || typeof b !== \"object\")\r\n    throw new TypeError(\"merge() - 'b' has to be an object, not \" + typeof b + \".\");\r\n\r\n  var aIsArray = isArray(a);\r\n  var bIsArray = isArray(b);\r\n\r\n  if (aIsArray && bIsArray) {\r\n    // TODO: Not sure if this is allowed...\r\n    return a;\r\n  }\r\n\r\n  if (aIsArray || bIsArray)\r\n    throw new TypeError(\"merge() - Detected incompatible types.\");\r\n\r\n  for (var k in b) {\r\n    var aVal, bVal = b[k];\r\n    if (hasOwn.call(a, k)) {\r\n      aVal = a[k];\r\n      if (aVal != null && typeof aVal === \"object\" &&\r\n          bVal != null && typeof bVal === \"object\") {\r\n        merge(aVal, bVal);\r\n      }\r\n      else {\r\n        // Not sure what to do here, clone? \r\n        a[k] = clone(bVal);\r\n      }\r\n    }\r\n    else {\r\n      a[k] = clone(bVal);\r\n    }\r\n  }\r\n\r\n  return a;\r\n}\r\n\r\nconsole.log(merge(\r\n  {},\r\n  { a: true, b: 1 })\r\n);\r\n\r\nconsole.log(merge(\r\n  { a: 1, b: { c: 1 } },\r\n  { a: 42, b: { c: 42 } })\r\n);\r\n\r\nconsole.log(merge(\r\n  {},\r\n  { a: true, b: { c: { d: 42 } } })\r\n);\r\n```"}}