I have a REST API of classical actors that I want to visualize in Postman. The image URL of an actor is not in the API, so I will need to create a mashup from a combination of the core API and another API.
1. Prerequisites
The core API/endpoint is at http://henke.atwebpages.com/postman/actors/actors.json:
{ "area": { "name": "United States", "type": null }, "release-groups": [ { "primary-type": "Actor", "fullname": "Katharine Hepburn", "id": "Q56016", "born": "1907" }, { "primary-type": "Actor", "fullname": "Humphrey Bogart", "id": "Q16390", "born": "1899" } ], "country": "US", "name": "Classical Actors", "life-span": { "begin": "1899", "ended": true, "end": "2003" } }
The image URLs of Katharine Hepburn and Humphrey Bogart are at:
http://henke.atwebpages.com/postman/actors/coverart/Q56016.json and
http://henke.atwebpages.com/postman/actors/coverart/Q16390.json,
respectively.
The corresponding JSONS, Katharine Hepburn:
{ "images": [ { "front": true, "thumbnails": { "small": "https://upload.wiki [...] 220px-Tom_cruise_1989.jpg", "large": "https://upload.wiki [...] -TomCruiseDec08MTV_cropped.jpg" }, "back": false, "edit": 18084161 }, { "back": true, "edit": 39938947, "front": false, "thumbnails": { "small": "https://upload.wiki [...] -Katharine_Hepburn_promo_pic.jpg", "large": "https://upload.wiki [...] Tom_Cruise_by_Gage_Skidmore_2.jpg" } } ] }
and Humphrey Bogart:
{ "images": [ { "edit": 40403385, "back": true, "thumbnails": { "small": "https://upload.wiki [...] 220px-Humphrey_Bogart_1940.jpg", "large": "https://upload.wiki [...] px-TomCruiseByIanMorris2010.jpg" }, "front": false }, { "edit": 40403384, "back": false, "thumbnails": { "small": "https://upload.wiki [...] 220px-Tom_cruise_1989.jpg", "large": "https://upload.wiki [...] -TomCruiseDec08MTV_cropped.jpg" }, "front": true } ] }
where I have truncated the links of the images for better readability.
Note in the core API how each object/person has a unique id
(Q56016
for
Katharine Hepburn and Q16390
for Humphrey Bogart) and a fullname
.
The other endpoints have – for each object in the release-groups
array of
the core API – the same unique identifier, along with a link to an
image/portrait.
Thus, information from all three endpoints is needed to list each actor with a
matching image.
2. The desired resulting mashup
Obviously, the problem is solved if the data in the APIs can be combined together in such a way that – for each identifier – both the name and the image link are supplied:
[ { "name": "Katharine Hepburn", "image": "https://upload.wiki [...] -Katharine_Hepburn_promo_pic.jpg" }, { "name": "Humphrey Bogart", "image": "https://upload.wiki [...] 220px-Humphrey_Bogart_1940.jpg" } ]
Then it remains to visualize the data in Postman.
3. Methodology
I will write all code in a single Tests script of a Postman request. That request is just a dummy that serves no other purpose than to start running the Tests script.
To construct the mashup and then display the result, it would be convenient to use the well-known Fetch API and then get the images by using Promise.all.
One caveat is that Postman does not implement the Fetch API.
But luckily there is an answer
that explains how to mimic the fetch()
command in Postman.
It can be done as follows:
function fetch (url) { return new Promise((resolve, reject) => { pm.sendRequest(url, function (_, fetchResponse) { resolve(fetchResponse); }); }); } // ^^ No Fetch API in Postman! But see https://stackoverflow.com/a/67588692
Since this fetch()
function returns a promise, it should (hopefully) work
the same way as fetch()
in any modern web browser.
The rest of the Tests section should construct the result.
Note how Promise.all
needs to be chained/nested with the first request
fetch(urlOuter)
– because it needs data from it.
This is analogous to the second Stack Snippet of this answer.
Finally, the result should be visualized:
1
const lock = setTimeout(() => {}, 43210); const fullnames = []; const urls = []; const urlOuter = 'http://henke.atwebpages.com/postman/actors/actors.json'; fetch(urlOuter).then(responseO => responseO.json()).then(responseBodyO => { const tblHeader = responseBodyO.name; const actors = responseBodyO['release-groups']; for (const item of actors) { fullnames.push(item.fullname); urls.push('http://henke.atwebpages.com/postman/actors/coverart/' + item.id + '.json'); } return Promise.all(urls.map(url => fetch(url) .then(responseI => responseI.json()) .then(responseBodyI => responseBodyI.images.find(obj => obj.back === true).thumbnails.small))) .then(imageURLs => { clearTimeout(lock); // Unlock the timeout. const actorNames = fullnames.map(value => ({ name: value })); const actorImages = imageURLs.map(value => ({ image: value })); const actorsAndImages = actorNames.map( (item, i) => Object.assign({}, item, actorImages[i])); console.log('actorsAndImages:n' + JSON.stringify(actorsAndImages)); const template = `<table> <tr><th>` + tblHeader + `</th></tr> {{#each responseI}} <tr><td>{{name}}<br><img src="{{image}}"></td></tr> {{/each}} </table>`; pm.visualizer.set(template, { responseI: actorsAndImages }); }); }).catch(_ => { console.error('Failed to fetch - ' + urlOuter); });
In Postman:
4. Does it work?
So does it work? – The answer is both yes and no.
- On the good side, I could create the desired JSON mashup result as in section 2 above.
- On the bad side, the visualization fails:
The message Set up the visualizer for this request is typical when the
call to pm.visualizer.set()
has been forgotten.
But I did not forget it. So what is wrong?
5. How to replicate my attempt in Postman
Replicating my attempt in Postman should be straightforward.
Assuming you are using the desktop version of Postman, do as follows:
Download and save
http://henke.atwebpages.com/postman/actors/Promise.all-Actors.pm_coll.json
in a suitable place on your hard drive.In Postman, Ctrl + O > Upload Files >
Promise.all-Actors.pm_coll.json
> Import.
You should now seePromise.all-Actors
among your collections in Postman.Collections >
Promise.all-Actors
>DummyRequest
> Send.In the Postman Response Body, click Visualize.
Done! – If everything worked as intended, you should now see the output as above.
References
- Wikidata on Katharine Hepburn
- Wikidata on Humphrey Bogart
- Postman does not implement the Fetch API
- Postman workaround for known bug: chained requests never getting executed
- How to run nested requests in JavaScript – 2nd snippet
- How can I fetch an array of URLs with Promise.all?
1 Don’t get confused by the lines
const lock = setTimeout(() => {}, 43210);
and clearTimeout(lock);
. –
Their only purpose is to serve as a workaround for a known bug.
Advertisement
Answer
The message Set up the visualizer for this request is typical when the call to
pm.visualizer.set()
has been forgotten. But I did not forget it. So what is wrong?
As already touched upon, the problem is that
Postman does not natively support promises.
1
What does that mean? – Well, apparently it means that a function such as
pm.visualizer.set()
cannot be called from within the callback of a
Promise.
It has to be called from within the callback of pm.sendRequest()
.
Note that by the construction of the fetch()
function the corresponding
Promise is actually outside of the pm.sendRequest()
callback!
1. Achieving the desired result and visualizing it
In other words, you need to replace all occurrences of fetch()
with
pm.sendRequest()
.
You also need to implement your own version of Promise.all
, since it relies
upon promises, something you don’t have in a native Postman script.
Fortunately, such an implementation was posted in
an answer the day before yesterday.
After making those changes, here is the code for the Tests section, starting with the initializations: 2
const lock = setTimeout(() => {}, 43210); const fullnames = []; const urls = []; const urlOuter = 'http://henke.atwebpages.com/postman/actors/actors.json';
The main part – slightly unconventionally formatted – to avoid vertical scrolling:
pm.sendRequest(urlOuter, (_, responseO) => { const tblHeader = responseO.json().name; const actors = responseO.json()['release-groups']; for (const item of actors) { fullnames.push(item.fullname); urls.push('http://henke.atwebpages.com/postman/actors/coverart/' + item.id + '.json'); } const images = []; let countDown = urls.length; urls.forEach((url, index) => { asynchronousCall(url, imageURL => { images[index] = imageURL; if (--countDown === 0) { // Callback for ALL starts on next line. clearTimeout(lock); // Unlock the timeout. const actorNames = fullnames.map(value => ({ name: value })); const actorImages = images.map(value => ({ image: value })); const actorsAndImages = actorNames.map( (item, i) => Object.assign({}, item, actorImages[i])); console.log('actorsAndImages:n' + JSON.stringify(actorsAndImages)); const template = `<table> <tr><th>` + tblHeader + `</th></tr> {{#each responseI}} <tr><td>{{name}}<br><img src="{{image}}"></td></tr> {{/each}} </table>`; pm.visualizer.set(template, { responseI: actorsAndImages }); } }); }); function asynchronousCall (url, callback) { pm.sendRequest(url, (_, responseI) => { callback(responseI.json().images.find(obj => obj.back === true) .thumbnails.small); // Individual callback. }); } });
In Postman:
2. Does it work?
Yes! – It works:
3. How to replicate my solution in Postman
Assuming you are using the desktop version of Postman, do as follows:
Download and save
http://henke.atwebpages.com/postman/actors/Actors.pm_coll.json
in a suitable place on your hard drive.In Postman, Ctrl + O > Upload Files >
Actors.pm_coll.json
> Import.Collections >
Actors
>DummyRequest
> Send.In the Postman Response Body, click Visualize.
Done! – You should now see the output as above.
References
pm.sendRequest
returns a pm object – not a Promise- How to fetch an array of URLs without Promise.all
1 I hope Postman will support promises in a future version.
2 Again, don’t get confused by the lines
const lock = setTimeout(() => {}, 43210);
and clearTimeout(lock);
. –
Their only purpose is to serve as a workaround for a known bug.