Zapier - add data to JSON response (App development) - zapier

We are creating a Zapier app to expose our APIs to the public, so anyone can use it. The main endpoint that people are using returns a very large and complex JSON object. Zapier, it looks like, has a really difficult time parsing nested complex JSON. But it does wonderful with a very simple response object such as
{ "field": "value" }
Our data that is being returned has this structure and we want to move some of the fields to the root of the response so it's easily parsed by Zapier.
"networkSections": [
{
"identifier": "Deductible",
"label": "Deductible",
"inNetworkParameters": [
{
"key": "Annual",
"value": " 600.00",
"message": null,
"otherInfo": null
},
{
"key": "Remaining",
"value": " 600.00",
"message": null,
"otherInfo": null
}
],
"outNetworkParameters": null
},
So, can we do something to return for example the remaining deductible?
I got this far (adding outputFields) but this returns an array of values. I'm not sure how to parse through this array either in the Zap or in the App.
{key: 'networkSections[]inNetworkParameters[]key', label: 'xNetworkSectionsKey',type: 'string'},
ie this returns an array of "Annual", "Remaining", etc

Great question. In this case, there's a lot going on, and outputFields can't quite handle it all. :(
In your example, inNetworkParameters contains an array of objects. Throughout our documentation, we refer to these as line items. These lines items can be passed to other actions, but the different expected structures presents a bit of a problem. The way we've handled this is by letting users map line-items from one step's output to another step's input per field. So if step 1 returns
{
"some_array": [
{
"some_key": "some_value"
}
]
}
and the next step needs to send
{
"data": [
{
"some_other_key": "some_value"
}
]
}
users can accomplish that by mapping some_array.some_key to data.some_other_key.
All of that being said, if you want to always return a Remaining Deductible object, you'll have to do it by modifying the result object itself. As long as this data is always in that same order, you can do something akin to
var data = z.JSON.parse(bundle.response.content);
data["Remaining Deductible"] = data.networkSections[0].inNetworkParameters[1].value;
return data;
If the order differs, you'll have to implement some sort of search to find the objects you'd like to return.
I hope that all helps!

Caleb got me where I wanted to go. For completeness this is the solution.
In the creates directory I have a js file for the actual call. The perform part is below.
perform: (z, bundle) => {
const promise = z.request({
url: 'https://api.example.com/API/Example/' + bundle.inputData.elgRequestID,
method: 'GET',
headers: {
'content-type': 'application/json',
}
});
return promise.then(function(result) {
var data = JSON.parse(result.content);
for (var i=0; i<data.networkSections.length; i++) {
for (var j=0; j<data.networkSections[i].inNetworkParameters.length; j++) {
// DEDUCT
if (data.networkSections[i].identifier == "Deductible" &&
data.networkSections[i].inNetworkParameters[j].key == "Annual")
data["zAnnual Deductible"] = data.networkSections[i].inNetworkParameters[j].value;
} // inner for
} // outer for
return data;
});

Related

How to use 2 conditions inside the elastic watcher

I'm new to ELK, can i use 2 conditions in Elastic watchers. I am getting a field from logs like data = 0 and data = 1 so i need to use that "data" as condition inside my watcher to elobarate the events.
Thanks in advance
There's many solutions. Here's one using painless script:
[query sections...]
},
"condition": {
"script": {
"source": """
def obj = ctx.payload.hits.hits.0;
if (obj.data.value == 0 || obj.data.value == 1) {
return true;
}
return false;
""",
"lang": "painless"
}
},
"actions": {
[actions sections to follow...]
Of course I'm only making up the CTX context data path. In the example, I am referring to the "data" field of the first returned record. You will have to figure out what you want to check. One common piece of data is from aggregations, then you will have a to access ctx.payload.aggregations.*

Twilio Studio - Select and Parse JSON Using Liquid

In Twilio Studio, I'm making a GET request and am trying to parse JSON and subsequently assign variables based on the parsed JSON. I'm having difficulty doing so with the JSON that is returned.
Essentially I'm trying to set variables from the "Row" that matches the returned JSON (a user dials in, enters their PIN {{widgets.PIN_Entry.Digits}}, the PIN will match a "Row" in the returned JSON from the GET request and we set variables for userID, userEmail, userName, userPin for the matched row).
{
"DataSource": {
"Id": "12345",
"Name": "Dial-In Subscribers",
"Rows": [
[
"EMP-0226",
"ron#pawneeil.com",
"Ron Swanson",
"00054321"
],
[
"EMP-0267",
"leslie#pawneeil.com",
"Leslie Knope",
"00012345"
]
],
"TotalRows": 2,
"LastUpdated": "2020-08-26T03:39:42.7670000Z",
"CompanyId": 12345
}
}
I can easily do this with JSON Path (not supported by Twilio studio) to select the values I'm looking to set as variables, but I can't figure out how to use Liquid to do this.
userID == $.DataSource.Rows[?(#.includes('00012345'))].[0]
(would return "EMP-0267")
userEmail == $.DataSource.Rows[?(#.includes('00012345'))].[1]
(would return "leslie#pawneeil.com")
userName == $.DataSource.Rows[?(#.includes('00012345'))].[2]
(would return "Leslie Knope)
userPin == $.DataSource.Rows[?(#.includes('00012345'))].[3]
(would return "00012345")
Can anyone share some ideas on how to parse the JSON and set variables using Liquid? Here's how I'm thinking I would accomplish this:
Match the variable {{widgets.PIN_Entry.Digits}} to a row in the returned JSON
Parse the selected row and set variables for userID, userEmail, userName, userPin.
I use the Run Function Widget in these cases, I find it much easier to deal with then the nuances of Liquid Syntax.
// Description
// Make a read request to an external API
// Add axios 0.20.0 as a dependency under Functions Settings, Dependencies
const axios = require('axios');
exports.handler = function (context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
// Arrays start at 0
let selectedDigit = 0;
axios
.get(`https://x.x.x.x/myAPI`)
.then((response) => {
let { Rows } = response.data.DataSource;
let result = Rows.filter((record, index) => index === selectedDigit);
twiml.say(`The result is ${result}`);
return callback(null, twiml);
})
.catch((error) => {
console.log(error);
return callback(error);
});
};

How to get a sub-field of a struct type map, in the search response of YQL query in Vespa?

Sample Data:
"fields": {
"key1":0,
"key2":"no",
"Lang": {
"en": {
"firstName": "Vikrant",
"lastName":"Thakur"
},
"ch": {
"firstName": "维克兰特",
"lastName":"塔库尔"
}
}
}
Expected Response:
"fields": {
"Lang": {
"en": {
"firstName": "Vikrant",
"lastName":"Thakur"
}
}
}
I have added the following in my search-definition demo.sd:
struct lang {
field firstName type string {}
field lastName type string {}
}
field Lang type map <string, lang> {
indexing: summary
struct-field key {
indexing: summary | index | attribute
}
}
I want to write a yql query something like this (This doesn't work):
http://localhost:8080/search/?yql=select Lang.en from sources demo where key2 contains 'no';
My temporary workaround approach
I have implemented a custom searcher in MySearcher.java, through which I am able to extract the required sub-field and set a new field 'defaultLang', and remove the 'Lang' field. The response generated by the searcher:
"fields": {
"defaultLang": {
"firstName": "Vikrant",
"lastName":"Thakur"
}
}
I have written the following in MySearcher.java:
for (Hit hit: result.hits()) {
String language = "en"; //temporarily hard-coded
StructuredData Lang = (StructuredData) hit.getField("Lang");
Inspector o = Lang.inspect();
for (int j=0;j<o.entryCount();j++){
if (o.entry(j).field("key").asString("").equals(language)){
SlimeAdapter value = (SlimeAdapter) o.entry(j).field("value");
hit.setField("defaultLang",value);
break;
}
}
hit.removeField("Lang");
}
Edit-1: A more efficient way instead is to make use of the Inspectable interface and Inspector, like above (Thanks to #Jo Kristian Bergum)
But, in the above code, I am having to loop through all the languages to filter out the required one. I want to avoid this O(n) time-complexity and make use of the map structure to access it in O(1). (Because the languages may increase to 1000, and this would be done for each hit.)
All this is due to the StructuredData data type I am getting in the results. StructureData doesn't keep the Map Structure and rather gives an array of JSON like:
[{
"key": "en",
"value": {
"firstName": "Vikrant",
"lastName": "Thakur"
}
}, {
"key": "ch",
"value": {
"firstName": "维克兰特",
"lastName": "塔库尔"
}
}]
Please, suggest a better approach altogether, or any help with my current one. Both are appreciated.
The YQL sample query I guess is to illustrate what you want as that syntax is not valid. Picking a given key from the field Lang of type map can be done as you do in your searcher but deserializing into JSON and parsing the JSON is probably inefficient as StructuredData implements the Inspectable interface and you can inspect it directly without the need to go through JSON format. See https://docs.vespa.ai/documentation/reference/inspecting-structured-data.html

Automl image prediction problems

I get different results when using a model to get image annotation predictions from web UI and from API. Specifically, using the web UI I actually get predictions, but using the API I get nothing - just empty output.
It's this one that gives nothing using the API: https://cloud.google.com/vision/automl/docs/predict#automl-nl-example-cli
Specifically, the return value is {} - an empty JS object. So, the call goes through just fine, there's just no output.
Any hints as to how to debug the issue?
By default only results with prediction score > 0.5 are returned by the API.
To get all predictions you will need to provide extra argument 'score_threshold' to predict request:
For the REST API:
{
"payload": {
"image": {
"imageBytes": "YOUR_IMAGE_BYTES"
},
"params": { "score_threshold": "0.0" },
}
}
For the python call:
payload = {'image': {'image_bytes': content }, "params": { "score_threshold": "0.0" }}
With this argument all predictions will be returned. The predictions will be ordered by the 'score'.
Hope that helps,
That doesn't work, at least at the moment.
Instead the params need to go at the same level as the payload. E.g.:
{
"payload": {
"image": {
"imageBytes": "YOUR_IMAGE_BYTES"
}
},
"params": { "score_threshold": "0.0" },
}

Laravel 5 dingo api, add multiple transformed objects to the response

Want to add a transformed object along with other response, I have used following code:
$accessToken = Authorizer::issueAccessToken();
$user = User::where('email', $request->get('username'))->with('profile')->first();
if ($user) {
$accessToken['user'] = $this->response->item($user, new UserTransformer);
}
return $accessToken;
Expected Response:
{
"access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 31536000,
"data": {
"id": 1,
"email": "xxxxx",
"profile": {
"data": {
"id": 1,
"first_name": "Muhammad",
"last_name": "Shakeel",
}
}
}
}
but not getting transformed object, there must be some better way to add multiple transformed objects with response. Am I missing something?
Edit
Current response returns user object without transformation and if I return only user transformed object like following, it returns correct transformed object:
return $this->response->item($user, new UserTransformer);
As discussed on the issue tracker(https://github.com/dingo/api/issues/743#issuecomment-160514245), jason lewis responded to the ticket with following:
The only way you could do this at the moment would be to reverse that. So you'd return the response item, then add in the access token data, probably as meta data.
So, something like this.
return $this->response->item($user, new UserTransformer)->setMeta($accessToken);
The response will then contain a meta data key which will contain your access token data.
I got it to work using Internal Requests. https://github.com/dingo/api/wiki/Internal-Requests
So what you can do is
Suppose you have a route that fetches transformed user object at api/users/{email_id}?access_token=...
While issuing the access_token you can do the following :
$dispatcher = app('Dingo\Api\Dispatcher');
$array = Authorizer::issueAccessToken();
$array['user'] = $dispatcher->get('api/users/'.$request->get("username").'?access_token='.$array['access_token']);
return $array;
This will return transformed data.
NOTE : You will need to have a route that fetches user data.
You will have to handle cases in /api/users/{email-id} where email-id does not exist.

Resources