How to use nested loops for fetching the data from a rest api in flutter? - api

I have a order screen where the placed orders and the items that are placed for each order is displayed. I have used a for loop to fetch the orders from the api but in the order json response it also has items parameter inside which there are multiple items. I am not able to figure out how to place another loop in the code to fetch the items list. So please help me with it...
Thank you..
my json response
[
{
"id": 1453,
"total": "407.00",
"line_items": [
{
"id": 34,
"name": "Aloo Chaat Salad",
"product_id": 931,
"quantity": 1,
"total": "90.00",
},
{
"id": 35,
"name": "Aloo Jeera",
"product_id": 1020,
"quantity": 1,
"total": "140.00",
},
{
"id": 36,
"name": "Banana Shake",
"product_id": 963,
"quantity": 1,
"tax_class": "",
"total": "140.00",
}
],
}
]
myModel.dart
class OrderListModel {
final int id;
final String total;
Map line_items = {};
OrderListModel(this.id, this.total, this.line_items);
}
my code for fetching the data
List<OrderListModel> myAllDatas = [];
Future getDatas() async {
String basicAuth = 'Basic ' +
base64.encode(
utf8.encode('${GlobalVar.consumerKey}:${GlobalVar.secretKey}'));
var response = await http
.get("${GlobalVar.url}wp-json/wc/v2/orders?customer=6", headers: {
'Authorization': basicAuth,
'Accept': 'application/json',
});
if (response.statusCode == 200) {
String responseBody = response.body;
var jsonBody = json.decode(responseBody);
for (var data in jsonBody) // loop for fetching the orders
{
myAllDatas.add(new OrderListModel(data['id'], data['total'],
data['line_items'])); // how to place a loop so that i can fetch the items
inside the line_items parameter too?
}
setState(() {});
} else {
print(response.statusCode);
print(response.body);
}
}
when I fetch the items of line_items i just want to fetch their names and seperate them by commas in a row.

Use for loop like this -
for (var data in body) {
List items = data["line_items"];
for (int i = 0; i < items.length; i++) {
int id = items[i]["id"];
String name = items[i]["name"];
}
}

Related

UKG Dimensions Number of employees in request (624) exceeds allowed limit (500)

This error occurs when you are requesting aggregated data using the URL:
POST - {{DIMENSIONSHOST}}/v1/commons/data/multi_read
The Postman body that I used was:
{
"select": [
{"key": "EMP_COMMON_FULL_NAME"}
],
"from": {
"view": "EMP",
"employeeSet": {
"hyperfind": {
"id": -9
},
"dateRange": {
"startDate": "2022-01-01",
"endDate": "2022-04-30"
}
}
},
"index": 0,
"count": 500
}
Notice that I requested "count": 500. Even though I requested only 500 records, I got the error message that there were more than 500 records.
This identifies a bug in UKG Dimensions. I have developed a work around:
Retrieve the hyperfind by itself using /v1/commons/hyperfind/execute
Use a Postman Test (a post-response program) to split the IDs into batches of 500.
Save the batches to an environment variable
Use the environment variable for the aggregated data request.
You can retrieve the hyperfind by itself using:
POST - {{DIMENSIONSHOST}}/v1/commons/hyperfind/execute
The body of the request is:
{
"dateRange": {
"startDate": "2022-05-01",
"endDate": "2022-06-30"
},
"hyperfind": {
"id": -9
},
"includeTerminatedInRangeForLocations": true
}
The test script is:
var jsonData = JSON.parse(responseBody); //the data from the response body
var allIDs = []; //an array of all the IDs, no record count limit
var max500IDs = []; //arrays of maximum number of IDs
//
//retrieve all the IDs and put them into an array called allIDs
//
for(var i = 0; i < jsonData.result.refs.length; i++) {
allIDs.push(jsonData.result.refs[i].id );
}
var batchCount = 1; //number of batches - default 1
var IDsInBatch = 500; //maximum number of records in batch
//
//calculate the number of batches that you will need
//
if(allIDs.length > IDsInBatch) {
batchCount = Math.ceil((allIDs.length - 1) / IDsInBatch);
}
//
//loop through the number of batches
//
var eeCountInOtherBatches = 0;
for(var k = 0; k < batchCount; k++) {
//
//loop through all the IDs and transfer them to a max 500 batch
//
var batch = []
for(var j = 0; j < IDsInBatch; j++) {
personID = allIDs[eeCountInOtherBatches + j];
if(personID) {
batch.push(personID);
}
}
max500IDs[k] = batch;
eeCountInOtherBatches = eeCountInOtherBatches + IDsInBatch;
}
//
//transfer the batches to environment variable(s)
//
for(var x = 0; x < max500IDs.length; x++) {
postman.setEnvironmentVariable("max500IDs_" + x, max500IDs[x]);
}
The environment variables will be:
max500IDs_0
max500IDs_1
etc.
The employee request would be something like:
POST - {{DIMENSIONSHOST}}/v1/commons/data/multi_read
The body would be:
{
"select": [
{"key": "EMP_COMMON_FULL_NAME"}
],
"from": {
"view": "EMP",
"employeeSet": {
"employees": {
"ids": [{{max500IDs_0}}]
},
"dateRange": {
"startDate": "2022-01-01",
"endDate": "2022-04-30"
}
}
},
"index": 0,
"count": 500
}

Node.js Express resultset from async function not available as function value

In a Node Express app alternative products are connected to products by product Id. The alternative products (in a separate table) are right joined to the Products on ProductAlternatives.AlternativeProductId=Products.Id. I am trying to generate a nested JSON structure where alternative products (if available) are shown in a separate array.
What I get as a result is a flat JSON structure like this:
[
{
"Id": 1,
"ProductName": "2 Zits My Style",
"Description": "2 zits | stof | Stof grof geweven lichtgrijs",
"Price": "579.00",
"Picture": "~/UserImages/8195299-254-02.jpg",
"CatID": 5,
"alternateproducts": {}
},
{
"Id": 4,
"ProductName": "Slaapbank Hervik",
"Description": "Cubos 50 lever",
"Price": "1.499.00",
"Picture": "~/UserImages/8202978-06.jpg",
"CatID": 11,
"alternateproducts": {}
},
so, without the alternative products. The attribute alternateproducts for the first product should contain exactly 2 alternative products, like so:
[
{
"Id": 1,
"ProductName": "2 Zits My Style",
"Description": "2 zits | stof | Stof grof geweven lichtgrijs",
"Price": "579.00",
"Picture": "~/UserImages/8195299-254-02.jpg",
"CatID": 5,
"alternateproducts": {
[
{
"Id": 24,
"ProductName": "Zitbank Ravenia",
"Description": "2 zits | stof | Stof ribstof taupe",
"Price": "1.139.00",
"Picture": "~/UserImages/8192530-94-02.jpg",
"CatID": 5
},
{
"Id": 25,
"ProductName": " Zitbank Gino",
"Description": "2 zits | Stof velours okergeel",
"Price": "499.00",
"Picture": "~/UserImages/8194150-01.jpg",
"CatID": 5
}
]
}
},
{
"Id": 4,
"ProductName": "Slaapbank Hervik",
"Description": "Cubos 50 lever",
"Price": "1.499.00",
"Picture": "~/UserImages/8202978-06.jpg",
"CatID": 11
}
Code is as follows:
var structured;
// Application
app.route('/products')
// GET endpoint
.get(cors(), function(req, res) {
// create Request object
var request = new appmodule.sql.Request();
// query to the database and get all the records
request.query('SELECT * FROM Products', function (err, recordset) {
if (err) console.log(err)
GenerateStructuredJSON(recordset);
res.json(structured);
});
});
function GenerateStructuredJSON(recordset) {
var products = recordset.recordset;
structured = getNestedProducts(products);
function getNestedProducts(prod) {
for(var i = 0; i < prod.length; ++i) {
var productId = prod[i].Id;
var alternativeproducts = getAlternativeProducts(productId);
console.log(alternativeproducts); // logging nothing but Promise { <pending> }
if(alternativeproducts) {
prod[i].alternateproducts = alternativeproducts;
}
}
return prod;
}
}
async function getAlternativeProducts(prodID) {
const rs = await new Promise(function(resolve, reject,) {
var request = new appmodule.sql.Request();
request.input('prodid', sql.Int , prodID);
request.query('SELECT Products.* FROM Products ' +
'RIGHT JOIN ProductAlternatives ON ProductAlternatives.AlternativeProductId=Products.Id ' +
'WHERE ProductAlternatives.ProductId=#prodid', function(err, res) {
if (err) throw err
resolve(res.recordset);
})
});
console.log(rs); // logging json product structures correctly
return rs;
}
The strange thing is that the resultset rs from the async function is logged correctly with console.log(rs);, but as soon as it is connected to the var alternativeproducts in function getNestedProducts, it is logging nothing but empty Promise { <pending> }'s. So the resultset seems to be lost or unavailable at that time.
The reason is that async function in node.js is just a Promise. The line
var alternativeproducts = getAlternativeProducts(productId);
assigns the Promise to alternativeproducts.
You can make function GenerateStructuredJSON(recordset) async, then in your main function, you can then
await GenerateStructuredJSON(recordset)
But then you will need to await on all Promises from getAlternativeProducts created for each product.
I think a better approach is to have one sql to include the alternative product as part of the result using LEFT OUT jOIN. Then you will just need to structured the resulting JSON to the expected format.
Also, after
if (err) console.log(err)
it should not continue, i.e:
if (err) {
console.log(err);
return res.json(err);
}
Thanks for your assistance. I am relatively new to Express and I did not realize that async/await has to be implemented in order to get resultsets available and return them. I have made some adaptations to the code. The code is running without any problem now and is returning the product list including the alternative products as an array:
[
{
"Id": 1,
"ProductName": "2 Zits My Style",
"Description": "2 zits | stof | Stof grof geweven lichtgrijs",
"Price": "579.00",
"Picture": "~/UserImages/8195299-254-02.jpg",
"CatID": 5,
"alternateproducts": [
{
"Id": 24,
"ProductName": "Zitbank Ravenia",
"Description": "2 zits | stof | Stof ribstof taupe",
"Price": "1.139.00",
"Picture": "~/UserImages/8192530-94-02.jpg",
"CatID": 5
},
{
"Id": 25,
"ProductName": " Zitbank Gino",
"Description": "2 zits | Stof velours okergeel",
"Price": "499.00",
"Picture": "~/UserImages/8194150-01.jpg",
"CatID": 5
}
]
},
{
"Id": 4,
"ProductName": "Slaapbank Hervik",
"Description": "Cubos 50 lever",
"Price": "1.499.00",
"Picture": "~/UserImages/8202978-06.jpg",
"CatID": 11
},
{
"Id": 10,
"ProductName": "Slaapbank Larvik",
"Description": "Etnos 11 middengrijs/taupe",
"Price": "1.299.00",
"Picture": "~/UserImages/8201672-08.jpg",
"CatID": 11
},
But the code is relatively slow, it lasts 3 seconds to return data from 13 records. It might be an option to implement one JOIN query for all products and include the alternative products, and then use JSON AUTO for the json result.
Working code here:
// Application
app.route('/products')
// GET endpoint
.get(cors(), function(req, res) {
// create Request object
var request = new appmodule.sql.Request();
// query to the database and get all the records
request.query('SELECT * FROM Products', async function (err, recordset) {
if (err) console.log(err)
var outputstructure = await GenerateStructuredJSON(recordset); // wait for async function to return
res.json(outputstructure); // send outputstructure to AJAX method in html document
});
});
async function GenerateStructuredJSON(recordset) {
var products = recordset.recordset;
var structured = await getNestedProducts(products); // wait for getNestedProducts to return
return structured; // return the prod array (structured = prod)
async function getNestedProducts(prod) {
for(var i = 0; i < prod.length; ++i) {
var productId = prod[i].Id;
var alternativeproducts = await getAlternativeProducts(productId);
//console.log(alternativeproducts);
if (alternativeproducts != '') {
prod[i].alternateproducts = alternativeproducts;
}
}
return prod; // return full input array prod but with alternateproducts as additional attribute
}
}
async function getAlternativeProducts(prodID) {
const rs = await new Promise(function(resolve, reject,) {
var request = new appmodule.sql.Request();
request.input('prodid', sql.Int , prodID);
request.query('SELECT Products.* FROM Products ' +
'RIGHT JOIN ProductAlternatives ON ProductAlternatives.AlternativeProductId=Products.Id ' +
'WHERE ProductAlternatives.ProductId=#prodid', function(err, res) {
if (err) throw err
resolve(res.recordset); // resolve sets rs to value of res.recordset
})
});
//console.log(rs); // logging json product structures correctly
return rs;
}
Note that request.query and getNestedProducts(prod) are async now and getAlternativeProducts(productId) awaits the result from the async function.

Karate - filter a specific json key from response based on another static array

I have the following JSON response (reference name: "list") and
[
{
"key": "101",
"val": {
"portCall": {
"id": 12664978
},
"status": "in-port"
}
},
{
"key": "102",
"val": {
"portCall": {
"id": 12415798
},
"status": "in-port"
}
},
{
"key": "103",
"val": {
"status": "on-voyage",
"voyage": {
"id": "7kynv-7lq85"
}
}
},
{
"key": "104",
"val": {
"status": "on-voyage",
"voyage": {
"id": "7kynv-2385"
}
}
}
]
also, I have an array list of few key values, evImos = [101,102,104]
In that, I have to identify the first key in the "list" response that has status as "on-voyage". So, the result should be "104".
I have tried the following and I need some help to make it work. Any help would be appreciated.
* def function getFirst = function(evImos) { for (let num of evImos) { let results = list.filter(d => d["key"] === num && d["val"]["status"] === "on-voyage"); if(results.length === 1) { karate.log(num); return num; } } }
* list.forEach(getFirst(evImos))
I'll just give you one hint. This one line will convert the whole thing in to a form that is much easier for you to validate:
* def temp = {}
* list.forEach(x => temp[x.key] = x.val.status)
Which gives you:
{
"101": "in-port",
"102": "in-port",
"103": "on-voyage",
"104": "on-voyage"
}
Now you can do:
* def isOnVoyage = function(key){ return temp[key] == 'on-voyage' }
Also read this: https://stackoverflow.com/a/59162760/143475
Thanks, to #Peter.
Based on his hint, I just tweaked it a little bit to match my requirement and it worked for me.
Here is the working copy for anyone to refer in the future.
* def temp = {}
* list.forEach(x => temp[x.key] = x.val.status)
* def isOnVoyage = function(keys){ for (let key of keys) { if(temp[key] == 'on-voyage'){ karate.log(key); karate.set('num', key); break; }}}
* isOnVoyage(evImos)

Restsharp : Unable to pass following parameters in post body

I am trying to pass following parameters to request body but its not working :
{
"Start": 0,
"Limit": 10,
"SysID": 632;
"ResultScope": "Enrolled",
"SearchParams":
[
{"Field":"ID", "Weight": 1, "Value": ["1234567"], "Operation": "Active", "Enabled": true}
]
}
What i tried:
var test = request.AddBody(new Search { Start = 0, Limit = 10, SysId = 632, ResultScope = "Enrolled", SearchParams = new List<Object> { "ID", 1, 1234567, "Active”, "true" } });
but its not working. I am getting target parametercount exception when i am trying to add it to request as Addobject after seralization
You might want to read the docs.
request.AddJsonBody(
new Search {
Start = 0,
Limit = 10,
SysId = 632,
ResultScope = "Enrolled",
SearchParams = new List<Object> { "ID", 1, 1234567, "Active”, "true" }
});
var response = await client.GetAsync<WhateverYouWantBack>(request);

How do you get all the email body parts? And how do you know how many parts exist?

I'm trying to read emails responded by the Gmail API.
I have trouble accessing all the "parts". And don't have great ways to traverse through the response. I'm also lost as to how many parts can exist so that I can make sure I read the different email responses properly. I've shortened the response below...
{ "payload": { "mimeType": "multipart/mixed", "filename": "",
], "body": { "size": 0 }, "parts": [ {
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=\"us-ascii\""
},
{
"name": "Content-Transfer-Encoding",
"value": "quoted-printable"
}
],
"body": {
"size": 2317,
"data": "RGVhciBNSVQgQ2x1YiBWb2x1bnRlZXJzIGluIEFzaWEsDQoNCkJ5IG5vdyBlYWNoIG9mIHlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBpbnZpdGF0aW9ucyB0byB0aGUgcmVjZXB0aW9ucyBpbiBib3RoIFNpbmdhcG9yZSBhbmQgSG9uZyBLb25nIHdpdGggUHJlc2lkZW50IFJlaWYgb24gTm92ZW1iZXIgNyBhbmQgTm92ZW1iZXIg"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=\"us-ascii\""
},
{
"name": "Content-Transfer-Encoding",
"value": "quoted-printable"
}
],
"body": {
"size": 9116,
"data": "PGh0bWwgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwiIHhtbG5zOm89InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9mZmljZSIgeG1sbnM6dz0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTpvZmZpY2U6d29yZCIgeG1sbnM6bT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9vZmZpY2UvMjA"
}
}
] }, {
"partId": "1",
"mimeType": "text/plain",
"filename": "",
"body": {
"size": 411,
"data": "X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18NClRoYW5rIHlvdSBmb3IgYWxsb3dpbmcgdXMgdG8gcmVhY2ggeW91IGJ5IGVtYWlsLCB0aGUgbW9zdCBpbW1lZGlhdGUgbWVhbnMgZm9yIHNoYXJpbmcgaW5mb3JtYXRpb24gd2l0aCBNSVQgYWx1bW5pLiANCklmIHlvdSB3b3VsZCBsaWtlIHRvIHVuc3Vic2NyaWJlIGZyb20gdGhpcyBtYWlsaW5nIGxpc3Qgc2VuZCBhIGJsYW5rIGVtYWlsIHRvIGxpc3RfdW5zdWJzY3JpYmVAYWx1bS5taXQuZWR1IGFuZCBwdXQgdGhlIGxpc3QgbmFtZSBpbiB0aGUgc3ViamVjdCBsaW5lLg0KRm9yIGV4YW1wbGU6DQpUbzogbGlzdF91bnN1YnNjcmliZUBhbHVtLm1pdC5lZHUNCkNjOg0KU3ViamVjdDogYXNpYW9mZg0K"
} } ] } }
Is there something I'm missing?
A MIME message is not just an array it's a full blown tree structure. So you'll have to traverse it to correctly handle it. Luckily JSON parsers are plentiful and the problem can easily be handled with recursion. In many languages there exist very useful email parsing libraries that can make accessing traditional parts (e.g. the text/plain or text/html displayable part, or attachments) not too laborious.
You'll have to set up walker functions to traverse through the json and pick out the bits you are after. Here is a part of what I wrote. This may help you jumpstart your code. NOTE: this is used inside of wordpress...hence the special jQuery call. Not needed if you do not need to use jquery inside wordpress.
function makeApiCall() {
gapi.client.load('gmail', 'v1', function() {
//console.log('inside call: '+myquery);
var request = gapi.client.gmail.users.messages.list({
'userId': 'me',
'q': myquery
});
request.execute(function(resp) {
jQuery(document).ready(function($) {
//console.log(resp);
//$('.ASAP-emailhouse').height(300);
$.each(resp.messages, function(index, value){
messageId = value.id;
var messagerequest = gapi.client.gmail.users.messages.get({
'userId': 'me',
'id': messageId
});//end var message request
messagerequest.execute(function(messageresp) {
//console.log(messageresp);
$.each(messageresp, responsewalker);
function responsewalker(key, response){
messagedeets={};
$.each(messageresp.payload.headers, headerwalker);
function headerwalker(headerkey, header){
if(header.name =='Date'){
d = new Date(header.value);
var curr_date = d.getDate();
var curr_month = d.getMonth() + 1; //Months are zero based
var curr_year = d.getFullYear();
var formatteddate = curr_month+'/'+curr_date+'/'+curr_year;
messagedeets['date']=formatteddate;
//$('.ASAP-emailhouse').append('<p>'+header.value+'</p>');
}
if(header.name =='Subject'){
//console.log(header.value);
messagedeets.subject=header.value;
}
}
messagedeets.body = {};
$.each(messageresp.payload.parts, walker);
function walker(partskey, value) {
//console.log(value.body);
if (value.body.data !== "undefined") {
//console.log(value.body);
var messagebody = atob(value.body.data);
messagedeets.body.partskey = messagebody;
}
console.log(messagedeets);
$('.ASAP-emailhouse').append('<div class="messagedeets"><p class="message-date">'+messagedeets.date+': <span class="message-subject">'+messagedeets.subject+'</span></p><p>'+messagedeets.body.partskey+'</p></div>');
}//end responsewalker
//$('.ASAP-emailhouse').append('</li>');
}
//$('.ASAP-emailhouse').append('</ul>');
});//end message request
});//end each message id
});//end jquery wrapper for wordpress
});//end request execute list messages
});//end gapi client load gmail
}
The MIME parts you are looking for are in an array. JSON does not tell you up front how many items are in an array. Even MIME itself does not provide a way of knowing how many parts are present without looking at the entire message. You will just have to traverse the entire array to know how many parts are in it, and process each part as you encounter it.
To know how much parts exists, you can just use the Length property.
Example :
json.payload.parts.length
For your example, this property is 2 because there are 2 parts.