Forum Discussion

Garry_Gearhart's avatar
4 years ago

Postman API variable use

Has anyone successfully used variables with POST calls in Postman? I'm successful with basic tests creating groups and devices (no vars) but once variables are introduced in the place of values in the JSON body, postman console shows the data values are correctly being substituted but I'm getting 1401 errors.

  • 3 hours ago, Stuart Weenig said:

    Yes, I've not had any success using Postman as the thing that actually runs my API calls in production. I simply use it to figure out what my calls need to be. Once i get to the point where i need to loop through things with variables, i move to scripting. Powershell is great if that's your language. If Python's your language, consider the SDK.

    Thanks all. I thought something like that might be happening. The thing that still makes we scratch my head is I tried setting the ID in the pre-request script and it still didn't work. I was trying to put a collection together to ease the learning curve for some of the team. I'll just document it and let them insert their ID manually. 

  • Anonymous's avatar
    Anonymous

    Are you including the proper quotes around the variables to ensure the resulting json (after substitution) is valid? jsonlint.com?

  • This is the raw JSON format body in the request

    {
        "name": "{{groupName}}"
    }

    The postmaster console when I send shows this as the request body:

    {
        "name": "Huzzah"
    }

    But the response body returns:

    {"errorMessage":"Authentication failed","errorCode":1401,"errorDetail":null}

     

    If I change the raw JSON format body back to a string

    {
        "name": "Huzzah"
    }

    then it sends successfully and makes the group. No other changes are required in Postmaster. So if I trust Postmaster's console, the request outbound looks correct and identical with both requests.

  • Anonymous's avatar
    Anonymous

    That's a problem with your pre-request script? It's not doing authentication right?

  • I have what seems like a similar issue with a get operation getting a device by ID. When I set the ID manually in the request it returns a single device as expected. When I set the ID with an environment variable authentication fails with a 1401 code returned. The headers and request path look identical in both cases. Was there a resolution found for the original post?

    Working request with ID 5 set statically in the request:

    {{url}}/device/devices/5 sends: GET https://(company).logicmonitor.com/santaba/rest/device/devices/5

    Broken request with id variable set to 5:

    {{url}}/device/devices/{{id}} sends: GET https://(company).logicmonitor.com/santaba/rest/device/devices/5

    I tried modifying the pre-request script as well and had the exact same results. Script modification:

    var id = pm.environment.get('id');
    var request_vars = (http_verb == 'GET'||http_verb == 'DELETE') ?
    http_verb + epoch + resource_path + id: http_verb + epoch + request.data + resource_path;

     

  • Anonymous's avatar
    Anonymous

    I think this has more to do with how and when postman executes the pre-request script. The pre-request script calculates the signature, which has to contain the payload in the case of POST/PUT/PATCH requests. If the pre-request script runs before the resource path or body variable substitutions are done, then the signature generated by the pre-request script won't contain the final value, it'll contain the tokenized value.

    For example, when the pre-request script performs this line:

    var resource_path = request.url.replace(/(^{{url}})([^\?]+)(\?.*)?/, '$2');

    the value of request.url is still

    {{url}}/device/devices/{{id}}

    There's a little regex to ignore the {{url}} piece, but the id variable still hasn't been substituted in. So the signature is getting calculated using the wrong resource path. I would have suggested what you tried for the ID to fix it, but maybe there's a missing "/" between the resource_path and the id? Also, this change invalidates the pre-request script so that it only works with this call and not others.

    I imagine something similar is happening with the payload. Since your payload is equal to this:

    {
        "name": "{{groupName}}"
    }

    At the time the pre-request script runs, the signature is getting calculated incorrectly, based on the pre-variable insertion string, rather than the post-variable insertion string. When the pre-request script runs this line:

    var request_vars = (http_verb == 'GET'||http_verb == 'DELETE') ?
    http_verb + epoch + resource_path : http_verb + epoch + request.data + resource_path;

    I'm not sure how to fix it, but I imagine it would be something deep in Postman.

  • I moved to using Powershell. I believe the problem is with the body being explicitly RAW and JSON. I think it has to be RAW or maybe TEXT format to allow variable values to be referenced correctly and then converted to JSON via pre-request script but have not had a chance to test again. 

    5 hours ago, JonR said:

    I have what seems like a similar issue with a get operation getting a device by ID. When I set the ID manually in the request it returns a single device as expected. When I set the ID with an environment variable authentication fails with a 1401 code returned. The headers and request path look identical in both cases. Was there a resolution found for the original post?

    Working request with ID 5 set statically in the request:

    {{url}}/device/devices/5 sends: GET https://(company).logicmonitor.com/santaba/rest/device/devices/5

    Broken request with id variable set to 5:

    {{url}}/device/devices/{{id}} sends: GET https://(company).logicmonitor.com/santaba/rest/device/devices/5

    I tried modifying the pre-request script as well and had the exact same results. Script modification:

    var id = pm.environment.get('id');
    var request_vars = (http_verb == 'GET'||http_verb == 'DELETE') ?
    http_verb + epoch + resource_path + id: http_verb + epoch + request.data + resource_path;

     

    The following works for me (specific to JonR's request) 

    Pre-request script at the collection level:

    // Get API credentials from environment variables
    var api_id = pm.environment.get('api_id');
    var api_key = pm.environment.get('api_key');
    var dvId = pm.environment.get('dvId');
    var deviceId = pm.environment.get('deviceId')
    var dsId = pm.environment.get('dsId');
    var datasourceName = pm.environment.get('datasourceName');
    var instId = pm.environment.get('instId');
    
    // Get the HTTP method from the request
    var http_verb = request.method;
    // Extract the resource path from the request URL
    var resource_path = request.url.replace(/(^{{url}})([^\?]+)(\?.*)?/, '$2');
    var resource_path = resource_path.replace("{{dvId}}", dvId);
    var resource_path = resource_path.replace("{{dsId}}", dsId);
    var resource_path = resource_path.replace("{{datasourceName}}", datasourceName);
    var resource_path = resource_path.replace("{{instId}}", instId);
    var resource_path = resource_path.replace("{{deviceId}}", deviceId);
    
    console.log('resource_path: ' + resource_path)
    
    // Get the current time in epoch format
    var epoch = (new Date()).getTime();
    
    // If the request includes a payload, included it in the request variables
    var request_vars = (http_verb == 'GET'||http_verb == 'DELETE') ?
    http_verb + epoch + resource_path : http_verb + epoch + request.data + resource_path;
    
    // Generate the signature and build the Auth header
    var signature = btoa(CryptoJS.HmacSHA256(request_vars,api_key).toString());
    var auth = "LMv1 " + api_id + ":" + signature + ":" + epoch;
    
    // Write the Auth header to the environment variable
    pm.environment.set('auth', auth);

     

    Environment variables (pre-existing):

    api_id, api_key, auth, url, dvId, deviceId, dsld, datasourceName, instId (Current Value of dvId is set to a real device ID)

    Get request:

    {{url}}/device/devices/{{dvId}}

    Authorization tab, type is inherit.

    Headers tab key:Authorization, value:{{auth}} (also a good idea to set X-Version to 1 or 3 depending on what type of request for future reference. This works without it.)

    Pre-request script should have no content (if you are changing device id here, the request will fail as this runs after the collection level pre-request script but before the request is sent). 

  • Anonymous's avatar
    Anonymous

    Yes, I've not had any success using Postman as the thing that actually runs my API calls in production. I simply use it to figure out what my calls need to be. Once i get to the point where i need to loop through things with variables, i move to scripting. Powershell is great if that's your language. If Python's your language, consider the SDK.

  • I have come across this issue too when attempting to add an SDT to a device, it's quite frustrating. It's because the auth header is being constructed before the body has been fully constructed with other variables that are not yet known. It works if you are ok with static values.