Skip to content
This repository has been archived by the owner on Nov 23, 2021. It is now read-only.

Extend support for Postman random functions/dynamic variables/replaceIn #91

Closed
thim81 opened this issue May 4, 2021 · 13 comments
Closed

Comments

@thim81
Copy link
Contributor

thim81 commented May 4, 2021

Currently the package supports 3 random Postman functions.

  /* Version 4 GUID */
  guid() {
    return guid();
  },

  /* Random integer [0,1000] */
  randomInt() {
    return Math.floor(Math.random() * 1001);
  },

  /* Current time as Unix timestamp */
  timestamp() {
    return Date.now();
  }

Would you be open to extend this with more Postman dynamic variables?

https://learning.postman.com/docs/writing-scripts/script-references/variables-list/

For reference, here is the list of currently supported dynamic-variables provided by Postman.
https://github.com/postmanlabs/postman-collection/blob/6174b014607fef4bf571051c13160c621799597e/lib/superstring/dynamic-variables.js

@thim81
Copy link
Contributor Author

thim81 commented Jul 24, 2021

Created PR #92 to offer support for Postman random functions/dynamic variables

@gfeun
Copy link

gfeun commented Aug 4, 2021

Hey, thanks for the PR, that's really useful.

Do you think it would be possible to add support for the replaceIn function ? (https://learning.postman.com/docs/writing-scripts/script-references/variables-list/)
This is used to expand dynamic variables inside postman scripts (pre-request or tests).

I use it in pre-request script for "POST" requests in order to generate random fields that I can still access in "PUT" or "DELETE" requests later in the collection run.

Like so:

pm.variables.set("contact_email", pm.variables.replaceIn('{{$randomEmail}}'));
pm.variables.set("contact_number", pm.variables.replaceIn('{{$randomPhoneNumber}}'));

I can give a hand in implementing it or with testing but I'm not that familiar with JS so i'd be glad to have some pointers 🙂 .

@thim81
Copy link
Contributor Author

thim81 commented Aug 5, 2021

@gfeun i ll have a look at the replaceIn function and how it is build in Postman.

The use-case is clear and quite common in postman, not sure how K6 would handle it, since every iteration will be a new request.

Fyi: It might take some time before I ll be able to make some time for it.

@gfeun
Copy link

gfeun commented Aug 5, 2021

I forgot to mention that I use postman collections, so after generating with postman-to-k6, all my linked requests are wrapped in a k6 "group".

It turns out that pm.variables.set does not share variables to the next request in the group. But pm.collectionVariables.set does !

I can successfully retrieve previously set variables from the current iteration with pm.collectionVariables.get.

Postman handles variables a bit differently because a pm.variables.get will try to access variables from the lowest scope first climbing up to the globals. It seems that translated code to k6 does not do that.

I've made a test with a simple POST creating a ressource with a random ID that I store in a collection variable and was able to access the corresponding ID with a GET in a following request. It also works correctly with multiple VUs.

So the only missing piece is the replaceIn function.

Thanks for letting me know you're not available. I'll have a try at implementing the replaceIn, which does not seem that hard at first sight now that you have provided the various generators in your PR.

I'll keep you updated 🙂

@thim81
Copy link
Contributor Author

thim81 commented Aug 5, 2021

Curious how it goes. Are you going to start from the PR?

@gfeun
Copy link

gfeun commented Aug 5, 2021

Yes i'll start from your PR and will try this out tonight.

My plan would be to add the replaceIn function under variables here:

variables: Object.freeze({
get(name) {
return (
(scope.local[Has](name) && scope.local[name]) ||
(scope.data[Has](name) && scope.data[name]) ||
(scope.environment[Has](name) && scope.environment[name]) ||
(scope.collection[Has](name) && scope.collection[name]) ||
(scope.global[Has](name) && scope.global[name]) ||
undef
);
},
set(name, value) {
requireRequest();
scope.local = scope.local[Write](name, value);
},
}),

As a side note the implementation of set here makes it quite clear why variables set like this are not shared while the collectionVariables are:

set(name, value) {
requireRequest();
scope.collection = scope.collection[Write](name, value);
},

A naive implementation could be like:

replaceIn(template) {
  // Ugly search and replace based on the replace function
  // We have to add all generator type to the regex, quite ugly
  // We may use matchAll instead but this is a bit harder to juggle with indexes as we replace the template strings
  return template.replace(/{{\$timestamp}}|{{\$randomInt}}/g, function(match){     
    const generatorType = match.replace(/{{|}}/g, ''); // Extract generator name like "$randomName"
    return dynamic[generatorType].generator(); // Call the generator
  });
}

With template being a single {{$randomInt}} or something more complex like My name is {{$randomName}}.

The replaceIn implementation in Postman is quite alien to me but may eventually be useful as a reference: https://github.com/postmanlabs/postman-collection/blob/80b1af0c5daaa8a19c8a3eb9e7cce785e7a617e0/lib/collection/variable-scope.js#L302

@gfeun
Copy link

gfeun commented Aug 9, 2021

Hey,

I've pushed a PR on your fork for the replaceIn implementation so that it can eventually be added to your PR (thim81#1).
I added a Postman test collection to it for easy testing.

The implementation is quite straightforward.
As explained in my last post, i build a regex containing all generators names "randomName", "randomEmail" ... and use replace with it on the template string.

This may not be the fastest solution so i'm open to comments.

I also fixed the $guid method missing a return statement. It always returned undefined.

@thim81
Copy link
Contributor Author

thim81 commented Aug 9, 2021

@gfeun

this is a great addition. By end of this week (due to some time away from a laptop), I’ll be able to merge your PR in my PR and I ll push it to a release branch, so that you can already use it together with some bug fixes and contributions for others, while we wait for Grafana to do an official release.

@thim81 thim81 changed the title Extend support for Postman random functions/dynamic variables Extend support for Postman random functions/dynamic variables/replaceIn Aug 15, 2021
@thim81
Copy link
Contributor Author

thim81 commented Aug 18, 2021

@gfeun I have provided a forked NPM package version that contains all the open functional PR's (#92 / #103 / #113), including your replaceIn contribution and some dependencies bumps for security.

Replace in your packages.json:

"dependencies": {
    "postman-to-k6": "^1.5.0"
  }

to

"dependencies": {
    "@apideck/postman-to-k6": "^1.6.0"
  }

This is a forked NPM package, to allow users to use the new functionality. The current maintainers are quite overloaded by the acquisition of K6 by Grafana. By forking the repo & package, users can keep using new PR's & (security) fixes. At a later stage the changes can potentially be merged back in the original postman-to-k6 repo.

The changelog contains the differences between the original package & the forked version.

@thim81
Copy link
Contributor Author

thim81 commented Aug 18, 2021

@gfeun Do you have an example and the expected result of using the replaceIn? I want to create a test for it so that we can prevent regression in the future.

@gfeun
Copy link

gfeun commented Sep 2, 2021

I was on vacation sorry for the delay.

I thought about adding unit tests but since we generate random data it can get complicated.
We'd have to stub the generation functions.

Some example use:

console.log(replaceIn("{{$randomEmail}}")) // => abdc@efgh.com

console.log(replaceIn("My email is {{$randomEmail}}")) // => My email is ijkl@mnop.com

console.log(replaceIn("My name is {{$randomFirstName}} {{$randomLastName}}")) // => My name is Aaron Adams

I added an exemple postman request in my PR with example usage on httpbin.org if you want to test in a Postman context (https://github.com/apideck-libraries/postman-to-k6/blob/main/example/v2/replaceIn.json).

First i generate data in the pre-request script and put it in collectionVariables:

pm.collectionVariables.set("email", pm.variables.replaceIn("{{$randomEmail}}"));
pm.collectionVariables.set("name", pm.variables.replaceIn("{{$randomFirstName}}"));
pm.collectionVariables.set("phone_number", pm.variables.replaceIn("{{$randomPhoneNumber}}"));
pm.collectionVariables.set("id", pm.variables.replaceIn("{{$guid}}"));

They are then replaced in the request body:

{
    "email": "{{email}}",
    "name": "{{name}}",
    "phone_number": "{{phone_number}}",
    "id" : "{{id}}"
}

And i can test that it works with postman tests:

const resp = pm.response.json();

pm.test("Check response consistency", function () {
    pm.expect(resp.json.email).to.be.equal(pm.collectionVariables.get("email"));
    pm.expect(resp.json.name).to.be.equal(pm.collectionVariables.get("name"));
    pm.expect(resp.json.phone_number).to.be.equal(pm.collectionVariables.get("phone_number"));
});

@thim81
Copy link
Contributor Author

thim81 commented Sep 2, 2021

I wouldn't go a deep as testing the actual randomisation results since we are relying on the same script that is actually used in Postman. I would rather verify that the replaceIn function is injected in the generated K6 script.

I some how missed the https://github.com/apideck-libraries/postman-to-k6/blob/main/example/v2/replaceIn.json (thank you for pointing towards it), so I can use that to create a snapshot test of the generated result, to have some minimal coverage of the replaceIn method.

@ppcano
Copy link
Collaborator

ppcano commented Nov 22, 2021

Apologies for the inactivity on this project.

Due to the inability to properly support this project, the k6 team has decided not to continue its development. The primary reason is that k6 scope has grown significantly with the launch of k6 extensions. The team is prioritizing extending k6 capabilities over the converters.

We suggest you post your issue at api-deck/postman-to-k6. The project is active and maintained on this fork.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants