tejashah88/node-meraki-dashboard

dashboard.devices.update - does not update

nodevar opened this issue · 11 comments

Hi

Im not sure why but i can not update devices with the dashboard.devices.update function.
I do not get back an error, it just sends back the original object from the meraki api.

So my snippet goes like that:

`
params = {"name":"New Name", "lat":99.4180951010323, "lng":-99.098531723022, "serial":"QXXX-XXXX-XXXX", "mac":"XX:XX:XX:XX:XX:XX", "tags":" recently-added tag1 tag2 "};
console.log(JSON.stringify(params));

                            dashboard.devices.update(network_id, serial, params)
                                    .then(function(data) { console.log(data) })
                                    .catch(function(error) { console.log(error)});

Answer is like:
{"lanIp":"192.168.128.4","serial":"XXXX-XXXX-XXXX","mac":"XX:XX:XX:XX:XX:XX","lat":99.4180951010362,"lng":-99.098531723022,"address":"old","tags":" recently-added ","name":"Your AP","model":"MR32","networkId":"L_671036344471234567"}
`

If i put the params with Postmaster i get back the updated object.

So is anybody having the same issue?

Try running this code below and paste the output. It simply gets the device info before and after the update request. The RESPONSE and AFTER output should be the same.

dashboard.devices.get(network_id, serial)
    .then(function(data) {
        console.log("BEFORE");
        console.log(data);
        return dashboard.devices.update(network_id, serial, params);
    })
    .then(function(data) {
        console.log("RESPONSE");
        console.log(data);
        return dashboard.devices.get(network_id, serial);
    })
    .then(function(data) {
        console.log("AFTER");
        console.log(data);
    })
    .catch(function(error) { console.log(error) });

Thank you for the prompt reply

Output:

BEFORE
{ lanIp: '192.168.128.4',
serial: 'XXXX-XXXX-XXXX',
mac: ‘xx:xx:xx:xx:xx:xx’,
lat: 1.3682,
lng: 7.85128,
address: 'myAddress',
tags: ' tag1 tag2 tag3 gtag:on ',
name: 'MyAP',
model: 'MR32',
networkId: 'L_123456789123456789' }
RESPONSE
{ lanIp: '192.168.128.4',
serial: 'XXXX-XXXX-XXXX',
mac: 'exx:xx:xx:xx:xx:xx’',
lat: 1.3682,
lng: 7.85128,
address: 'myAddress',
tags: ' tag1 tag2 tag3 gtag:on ',
name: 'MyAP',
model: 'MR32',
networkId: 'L_123456789123456789' }
AFTER
{ lanIp: '192.168.128.4',
serial: ‘xx:xx:xx:xx:xx:xx’,
mac: 'e0:55:3d:1a:93:10',
lat: 1.3682,
lng: 7.85128,
address: 'myAddress',
tags: ' tag1 tag2 tag3 gtag:on ',
name: 'MyAP',
model: 'MR32',
networkId: 'L_123456789123456789' }

The Params are from the Meraki Sample request, with updated mac and serial:
https://dashboard.meraki.com/api_docs#update-the-attributes-of-a-device

'{"name":"Your AP", "lat":37.4180951010362, "lng":-122.098531723022, "serial":"XXXX-XXXX-XXXX", "mac":"xx:xx:xx:xx:xx:xx", "tags":" recently-added "}'

I'll admit, I'm a little surprised that it's acting this way. Unfortunately, I don't have a lot of time to try and resolve this issue, but anyone else looking can send PRs.

This is not a bug in your module, it looks like we are loosing somewhere in the redirects the Object/body of the PUT request. As a result you get back the actual, not updated Object from Meraki.

I used the exported NodeJS Native code from postman today and this was first not working either.

I got back a 307 Temporary Redirect and i investigated this further (with the module follow-redirects, a dependency of axios). However meraki is redirecting you from dashboard.meraki.com to api.meraki.com finally to n192.meraki.com:

  follow-redirects redirecting to https://api.meraki.com/api/v0/networks/network/devices/SERIAL +4s
  follow-redirects redirecting to https://api.meraki.com/api/v0/networks/network/devices/SERIAL +19ms
  follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/network/devices/SERIAL +1s
  follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/network/devices/SERIAL +5ms

What i know so far:

  • if you use https://api.meraki.com/api/v0/ as the basUrl you get back still just the old Object. Nothing will be updated.

  • If you don't use the "follow-redirects" module but the "https" module you get a html body (url api.meraki.com):

<html><body>You are being <a href="https://n192.meraki.com/api/v0/networks/network/devices/serial">redirected</a>.</body></html>

  • If you fire direct to n192.meraki.com you get back the updated Object and everything is working as expected.

  • If you do a GET request to the PUT URI you get back the Object from the Cloud as it is.

  • The docs mention only https://api.meraki.com/ as part of the baseUrl, not dashboard.meraki.com like used in the source here

What i can not prove yet:

  • If i use the follow-redirect library, somehow i lose the content to put within the redirects, probably the method is changed to GET?

~~I just see that i am probably wrong and this is not happening between the redirects, but i need some time to prove it. i update the thread as soon i have more answers.. ~~

Below the code i used for my tests , it looks like as soon there is a redirect the update does not work because the method is altered to GET.
The results are simliar to the output when using the node-meraki-dashboard library which uses then axios and this again uses the follow-redirects library

the code in follow-redirectes says:

183 if (response.statusCode !== 307 && !(this._options.method in SAFE_METHODS)) {
184 this._options.method = "GET";
185 // Drop a possible entity and headers related to it
186 this._requestBodyBuffers = [];
187 for (header in headers) {
188 if (/^content-/i.test(header)) {
189 delete headers[header];
190 }
191 }
192 }

I can put PUT into SAFE_METHODS and see then not anymore changed methods:

follow-redirects redirecting to http://dashboard.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX PUT 301 +5s
follow-redirects redirecting to https://api.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX PUT 307 +24s
follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX PUT 308 +2s

But still the Object is not updated. i dont know, probably i head over to the follow-redirect guys and update this case later…

In the follow-redirects module i updated the debug on line 206 from debug("redirecting to", redirectUrl); to debug("redirecting to", redirectUrl, this._options.method, response.statusCode );
In the Script below i changed hostname and var req to http or https to perform the tests


DEBUG LOG:
Request to dashboard.meraki.com with https.request the update fails


follow-redirects redirecting to https://api.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX PUT 307 +5s

follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 308 +3s


Request to dashboard.meraki.com with http.request the update fails


follow-redirects redirecting to https://api.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX PUT 307 +1s


follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 308 +2s


Request to api.meraki.com with http.request the update fails


follow-redirects redirecting to http://dashboard.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 301 +2s
follow-redirects redirecting to https://api.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 307 +648ms

follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 302 +2s


Request to api.meraki.com with https.request the update fails


follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 308 


Request to n192.meraki.com with https.request works 


Here no follow-redirects debug output


Request to n192.meraki.com with http.request the update fails 


follow-redirects redirecting to https://n192.meraki.com/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX GET 301 +513ms

CODE FOR TEST:
`
var http = require('follow-redirects').http;
var https = require('follow-redirects').https;

var options = {
"method": "PUT",
"hostname": "n192.meraki.com",
"port": null,
"path": "/api/v0/networks/L_1345678143456209123/devices/XXXX-XXXX-XXXX",
"headers": {
"x-cisco-meraki-api-key": apiKey,
"content-type": "application/json",
"cache-control": "no-cache",
"postman-token": "d708deed-b45a-6af5-6ad5-38b83a074a01"
}
}
var req = https.request(options, function (res) {
var chunks = [];

res.on("data", function (chunk) {
chunks.push(chunk);
});

res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});

req.write(JSON.stringify( { name: "TEST1“ }));
req.end();
`

First off, impressive job for your findings!

One thing I noticed is that the http code 308 is suggesting a "permanent redirect", which is slightly misleading since the final url for the Meraki DevNet Sandbox org was n149.meraki.com, while yours was n192.meraki.com for your organization.

Summary, workaround and fix:

The root of this is the redirect from dashboard.meraki.com to the the network pod.

If there is something else than Status 307 in the redirect chain, method PUT will not work.

So i put PUT in the SAFE Methods list:
var SAFE_METHODS = { PUT: true, GET: true, HEAD: true, OPTIONS: true, TRACE: true };
in follow-redirects/index.js

Unfortunately i see lowercase methods as you can see in this issue: follow-redirects/follow-redirects#82

So follow-redirects is expecting uppercase methods to compare them in the SAFE_METHODS list. In fact, i see only lowercase methods:
debug("response.statusCode: ", response.statusCode, "this._options.method : ", this._options.method, "SAFE_METHODS: ", SAFE_METHODS, "this._options.method in SAFE_METHODS: ", (this._options.method in SAFE_METHODS));

follow-redirects response.statusCode: 302 this._options.method : get SAFE_METHODS: { PUT: true, GET: true, HEAD: true, OPTIONS: true, TRACE: true } this._options.method in SAFE_METHODS: false +0ms

The upper/lowercase problem is not the case when using my script in the post above. It's only happening while using axios.

So in order to compare the SAFE_METHODS with this._options.method i make them uppercase:
if (response.statusCode !== 307 && !(this._options.method.toUpperCase() in SAFE_METHODS))

After, PUT works.

If you don't like to add PUT to your SAFE_METHODS list you can start to treat the 308 http code same as the 307 http code.
if (response.statusCode !== 307 && response.statusCode !== 308 && !(this._options.method.toUpperCase() in SAFE_METHODS))

However if you do this you can not use "hostname": "dashboard.meraki.com", you need to use api.meraki.com as your base url.

Yeah, that makes sense. Either way, the documentation uses api.meraki.com as the hostname. Expect a patch coming soon.

@nodevar version 1.0.0 has just been released, including the above change and many more

Love it! Thank you.