Time for another quick OpenWhisk tip. As you know (or may know!), when you create an OpenWhisk web action, you can pass parameters via the query string or via a form post. So consider the following trivial action:
function main(args) {
if(!args.name) args.name = 'Nameless';
let result = {
string:'Hello '+args.name
}
return {
headers: {
'Access-Control-Allow-Origin':'*',
'Content-Type':'application/json'
},
statusCode:200,
body: new Buffer(JSON.stringify(result)).toString('base64')
}
}
All this action does is say hello to a name that comes from the arguments passed to the function. After creating the action (and enabling web support), you can then hit it at your URL like so:
https://openwhisk.ng.bluemix.net/api/v1/web/rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest
And pass a name like so:
https://openwhisk.ng.bluemix.net/api/v1/web/rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest?name=Raymond+Camden
Cool. But what if you want to use the URL path instead of query parameters? Perhaps something like so:
https://openwhisk.ng.bluemix.net/api/v1/web/rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest/name/Raymond+Camden
The good news is that this is pretty simple to support. OpenWhish will pass this information to your action as args.__ow_path
. OpenWhisk actually passes a bunch of things you can read about here, but for our purposes, the __ow_path value is all we need. So consider this new version:
function main(args) {
if(args.__ow_path && args.__ow_path.length) {
/*
support /name/X only
*/
let parts = args.__ow_path.split('/');
if(parts.length === 3 && parts[1].toLowerCase() === 'name') {
args.name = decodeURIComponent(parts[2]);
}
}
if(!args.name) args.name = 'Nameless';
let result = {
string:'Hello '+args.name
}
return {
headers: {
'Access-Control-Allow-Origin':'*',
'Content-Type':'application/json'
},
statusCode:200,
body: new Buffer(JSON.stringify(result)).toString('base64')
}
}
All I've done is look for the path, see if it has length, and then I parse it. Now - in my particular case I'm assuming only one valid path: /name/X
. Obviously you could write the code to be a bit more generic, perhaps in the form of: /name/value/name/value
etc. But to keep it simple I just look for /name/X
and if that matches, set args.name to it. The result works perfectly:
Heh, oops. Almost. So you may have noticed I used decodeURIComponent
above, and it works correctly if you encode spaces with %20:
rcamden@us.ibm.com_My%20Space/safeToDelete/pathTest/name/Raymond%20Camden
So from what I've seen in my research, the plus sign is not meant to be decoded, and it may actually be part of the original string. So what you do here is up to you really. In this particular use case where I'm working with names, it would probably be safe to go ahead and replace plus signs with spaces:
args.name = decodeURIComponent(parts[2]).replace(/\+/g, ' ');
I hope this helps! As a quick aside, the URL in my tests includes safeToDelete
. That has nothing to do with the post. I'm just trying to use that package as a way to flag to myself actions I can safely delete later. (As you can imagine, I've got a bunch of crap up now on OpenWhisk and I'm starting to feel like I need to clean up a bit!)
Archived Comments
Hey Mate,
Thanks for the tips. How do you define the API gateway that calls this though?
I haven't tried it with the API Management feature yet. Is that what you meant - and have you tried it yourself yet to see what happens? It may "just work".
I gave it a go but cannot figure out how to do wildcards, so it seems you'd need to register every single variable path, clearly a blocker. Tried with path/{variable} which is pretty standard and works with AWS, but no. I think i'll have to use query string params, but that effectively means I'll need my services to handle multiple cases e.g. GET api/file?fileId=1234 would be used to get all files, and a specific file if an id is passed. Otherwise I'll have to do things like GET api/file/fileId?id=1234 which I find inelegant. I'm gonna go with option one for now I think as it will also keep the services a bit warmer.
Hmm. So to be clear, you defined an API Gateway and it gave you a url, lets say http://foo. Are you saying you can't use http://foo/zoo, ie just add /SOMETHING to the end?
Hey Mate, sorry for the delay. The problem is I can't put a dynamic value at the end, say an ID because there is no way to define a wildcard that I can see in API gateway. I'd have to explicitly map a URL for every ID, imagine the size of the swagger file!!!
Swagger does support dynamic values though. I'm definitely not a Swagger/OpenAPI expert, but I know that's supported. Something like {ID} or some such. Are you using the API gateway that is free and with OpenWhisk, or the "real" API Connect?
Yeah, swagger does but API gateway does not it seems. Would using API connect give me this functionality do you think?
I should know this and I don't - so I apologize - but you can try API Connect for free - so it's a risk free test. Can you give it a shot and report back?