I'm trying to build a custom connector on GDS for an external API.
I've configured the schema with all the fields coming from the API and I can see that correctly when I deploy and try the connector. However when I try to run the "Explore" I get a generic "Data Set Configuration Error - Data Studio cannot connect to your data set". I am passing an array of key value pairs as requested... so not sure what's going on.
This is the code I am using in the getData function
function getData(request) {
try {
request.configParams = validateConfig(request.configParams);
var requestedFields = getFields().forIds(
request.fields.map(function(field) {
return field.name;
})
);
var data = JSON.parse(jsonSample).Table
return {
schema: requestedFields.build(),
rows: data
};
}
catch (e) {
cc.newUserError()
.setDebugText('Error fetching data from API. Exception details: ' + e)
.setText(
'The connector has encountered an unrecoverable error. Please try again later, or file an issue if this error persists.'
)
.throwException();
}
}
Where jsonSample is a text string contain the following json (raw, not beautified):
{
"Table": [
{
"Entity": "Houston Heights",
"EntityD": "",
"Consolidation": "USD",
"ConsolidationD": "United States of America, Dollars",
"Scenario": "Actual",
"ScenarioD": "",
"Time": "2010M1",
"TimeD": "Jan 2010",
"View": "Periodic",
"ViewD": "",
"Account": "IFRS Balance Sheet",
"AccountD": "",
"Flow": "None",
"FlowD": "",
"Origin": "BeforeAdj",
"OriginD": "",
"IC": "None",
"ICD": "",
"UD1": "None",
"UD1D": "",
"UD2": "None",
"UD2D": "",
"UD3": "None",
"UD3D": "",
"UD4": "None",
"UD4D": "",
"UD5": "None",
"UD5D": "",
"UD6": "None",
"UD6D": "",
"UD7": "None",
"UD7D": "",
"UD8": "None",
"UD8D": "",
"CellValue": 2.25000000000000000000
},
{
"Entity": "Houston Heights",
"EntityD": "",
"Consolidation": "USD",
"ConsolidationD": "United States of America, Dollars",
"Scenario": "Actual",
"ScenarioD": "",
"Time": "2010M1",
"TimeD": "Jan 2010",
"View": "Periodic",
"ViewD": "",
"Account": "IFRS Balance Sheet",
"AccountD": "",
"Flow": "None",
"FlowD": "",
"Origin": "BeforeAdj",
"OriginD": "",
"IC": "None",
"ICD": "",
"UD1": "Admin",
"UD1D": "Admin",
"UD2": "None",
"UD2D": "",
"UD3": "None",
"UD3D": "",
"UD4": "None",
"UD4D": "",
"UD5": "None",
"UD5D": "",
"UD6": "None",
"UD6D": "",
"UD7": "None",
"UD7D": "",
"UD8": "None",
"UD8D": "",
"CellValue": 2.240000000000000000000
}
]
}
To solve this issue, the code should be like this because you are passing an array of objects:
function getData(request) {
try {
request.configParams = validateConfig(request.configParams);
var requestedFields = getFields().forIds(
request.fields.map(function (field) {
return field.name;
})
);
var data = JSON.parse(jsonSample).Table;
return {
schema: requestedFields.build(),
rows: data.map(function (row) {
var values = [];
requestedFields.asArray().forEach(function (field) {
values.push(row[field.getId()]);
});
return { values: values };
}),
};
} catch (e) {
cc.newUserError()
.setDebugText("Error fetching data from API. Exception details: " + e)
.setText(
"The connector has encountered an unrecoverable error. Please try again later, or file an issue if this error persists."
)
.throwException();
}
}
Related
I'm basically trying to bulk insert or update (on duplicate key) a JSON array of objects in a MySQL table using NodeJS. Sometimes JSON objects in array contain different key-value pairs.
A possible solution could be to loop over the JSON array first and then search each object for every key hardcoded from another array (corresponding to columns in table) then push result values in new array of arrays. In case a key does not exist in JSON object then fill with a "NULL" value.
Many thanks for any advice in advance!
var keys = ['customerId','subscriptionId','billingMethod','skuId','creationTime','planName','isCommitmentPlan','startTime','endTime','numberOfSeats','licensedNumberOfSeats','maximumNumberOfSeats','isInTrial','trialEndTime','renewalType','purchaseOrderId','status''customerDomain','skuName','suspensionReasons','dealCode'];
var demo_api = [{
"kind": "reseller#subscription",
"customerId": "C0123456",
"subscriptionId": "123",
"billingMethod": "ONLINE",
"skuId": "Google-Apps-Unlimited",
"creationTime": "1331647980142",
"plan": {
"planName": "ANNUAL",
"isCommitmentPlan": true,
"commitmentInterval": {
"startTime": "1331647980142",
"endTime": "1363183980142"
}
},
"seats": {
"kind": "subscriptions#seats",
"numberOfSeats": 10,
"licensedNumberOfSeats": 10,
"maximumNumberOfSeats": 500
},
"trialSettings": {
"isInTrial": false
},
"renewalSettings": {
"kind": "subscriptions#renewalSettings",
"renewalType": "SWITCH_TO_PAY_AS_YOU_GO"
},
"purchaseOrderId": "my_example.com_annual_1",
"status": "ACTIVE",
"customerDomain": "my_example.com",
"skuName": "G Suite Business"
},
{
"kind": "reseller#subscription",
"customerId": "D0123456",
"subscriptionId": "456",
"billingMethod": "ONLINE",
"skuId": "Google-Apps-For-Business",
"creationTime": "1331647980142",
"plan": {
"planName": "FLEXIBLE",
"isCommitmentPlan": false
},
"seats": {
"kind": "subscriptions#seats",
"licensedNumberOfSeats": 0,
"maximumNumberOfSeats": 10
},
"trialSettings": {
"isInTrial": false
},
"purchaseOrderId": "my_example_flex_1",
"status": "ACTIVE",
"customerDomain": "my_example2.com",
"skuName": "G Suite Business"
}];
Desired result:
var result = [
["C0123456", "123", "ONLINE", "Google-Apps-Unlimited", "1331647980142", "ANNUAL", true, "1331647980142", "1363183980142", 10, 10, 500, false, "NULL", "SWITCH_TO_PAY_AS_YOU_GO", "my_example.com_annual_1", "ACTIVE", "my_example.com", "G Suite Basic", "NULL", "NULL"],
["D0123456", "456", "ONLINE", "Google-Apps-For-Business", "1331647980142", "FLEXIBLE", false, "NULL", "NULL", "NULL", 0, 0, false, "NULL", "NULL", "my_example_flex_1", "ACTIVE", "my_example2.com", "G Suite Business", "NULL", "NULL"]
];
you can try something like this:
var myArray = new Array();
data.forEach((player) => {
console.log(player.id);
console.log(player);
var playerModel ={
id : player.id,
firstname : player.firstname,
lastname : player.lastname,
position : player.position,
price : player.price,
appearences : player.appearences,
goals : player.goals,
assists : player.assists,
cleansheets : player.cleansheets,
redcards : player.redcards,
yellowcards : player.yellowcards,
image : player.image,
clubid : player.clubid,
};
console.log("model"+playerModel.position);
myArray.push(playerModel);
});
Here you need to change the model player in my code by a model created by you , and replace like this, just follow your json endpoint :
var Mymodel = {
bind: player.bind,
............
....
}
the last step is that myArray is your results array , you need to loop it to see results
for(let item of myArray) {
console.log(item);
console.log(item.bind);
.......................
}
I'm having hard time in success to iterate over my external json file in Vue.
I'm importing the file like this:
import json from '../../public/platform.json'
export default {
data: () => ({
currentPage: 0,
brand: '',
platform: '',
affiliate: '',
myJson: json,
}),
Json file looking like this:
{
"Example": {
"Username": "",
"Password": "",
"AffiliateID": "",
"GI": "",
"CI": "",
"freeTextArea": ""
},
"ExampleTwo": {
"Username": "",
"Password": "",
"freeTextArea": ""
}
}
My goal is to do as follows:
I want to check if the "platform" from data is matching "Example" or "ExampleTwo" and if it does, I want to access the fields within either of them.
How can I do it?
You can use a computed property as follows:
computed: {
myPlatform: function () { return json[this.platform] || {}; },
}
Here is a demo: https://codesandbox.io/s/clever-gould-3hkbl?fontsize=14&hidenavigation=1&theme=dark
Goal:
Im trying to make an array of objetcs including 3 informations: id, title and imageUri. But when i try to get the imageUri value from firebase(an image download URL from firebase) to download this image in another component, the iteration of forEach freezes. Thank you for your time :)
Warning:[Unhandled promise rejection: TypeError: JSON.stringify cannot serialize cyclic structures.]
observation: When i remove the firebase part imageUri: firebase..., the whole thing works!
the function:
processData = ( data ) => {
console.log('---------data received from loadData(Main.js:70)--------\n', data)
var localProcessedData = [];
Object.entries(data).forEach( ([key, value]) => {
var event = {
id: key,
title: Object.getOwnPropertyDescriptor(value, "eventTitle").value,
imageUri: firebase.storage().ref('events/active/' + key + '/image').getDownloadURL()
}
localProcessedData.push(event);
})
this.setState({
processedData: localProcessedData,
eventsDataIsLoaded: true,
})
}
The type of params the function recieve:
Object {
"-M-I83aV9t1fezOsBn17": Object {
"active": true,
"created": "2020-02-05T02:18:30.772Z",
"description": "Olimpiadas Inter Atletica",
"eventTitle": "oia",
"location": "Uberlandia",
"owner": "p87xn6x8DZTwb6qyTadhkk3UxJV2",
"price": "130",
"startDate": "15",
"time": "14",
"updated": "2020-02-05T02:18:30.772Z",
},
"-M-KlUH-zQhnIhb6wMH8": Object {
"active": true,
"created": "2020-02-05T14:34:20.399Z",
"description": "Cia 2020",
"eventTitle": "Cia",
"location": "Uberlandia",
"owner": "p87xn6x8DZTwb6qyTadhkk3UxJV2",
"price": "130340",
"startDate": "15",
"time": "14",
"updated": "2020-02-05T14:34:20.399Z",
}
}
Expected:
My goal is to transform that whole data in an array like this:
Array [
Object {
"id": "-M-I83aV9t1fezOsBn17",
"title": "oia",
"imageUri": "image url from firebase"
},
Object {
"id": "-M-KlUH-zQhnIhb6wMH8",
"title": "Cia",
"imageUri": "image url from firebase"
}
]
Based on firebase documentation. FIrebase Storage getDownloadUrl is a promise
https://firebase.google.com/docs/reference/js/firebase.storage.Reference.html#get-downloadurl
solution is to implement an async/await
async processData = ( data ) => {
console.log('---------data received from loadData(Main.js:70)--------\n', data)
var localProcessedData = [];
Object.entries(data).forEach( async([key, value]) => {
var event = {
id: key,
title: Object.getOwnPropertyDescriptor(value, "eventTitle").value,
imageUri: await firebase.storage().ref('events/active/' + key + '/image').getDownloadURL()
}
localProcessedData.push(event);
})
this.setState({
processedData: localProcessedData,
eventsDataIsLoaded: true,
})
}
this code is not yet tested.
I receive a JSON result message in the following format from an old database query that I do not have the ability to change at this time:
{
"vsm1": "2429",
"vsm2": "2488",
"vsm3": "1968",
"vsm4": "",
"vsm5": "",
"vsm6": "",
"vsm7": "",
"vsm8": "",
"vsm9": "",
"vsm10": "",
"color1": "5",
"color2": "4",
"color3": "4",
"color4": "0",
"color5": "0",
"color6": "0",
"color7": "0",
"color8": "0",
"color9": "0",
"color10": "0",
"p1mtime": "1549296004",
"p2mtime": "1549296009",
"p3mtime": "1549296014",
"p4mtime": "",
"p5mtime": "",
"p6mtime": "",
"p7mtime": "",
"p8mtime": "",
"p9mtime": "",
"p10mtime": "",
"inch1": "",
"inch2": "",
"inch3": "",
"inch4": "",
"inch5": "",
"inch6": "",
"inch7": "",
"inch8": "",
"inch9": "",
"inch10": ""
}
I would like to re-format it to a more useable object, like so:
{ id: 1, vsm: 2429, color: 5, pmtime: 1549296004, inch: 0 }
{ id: 2, vsm: 2488, color: 4, pmtime: 1549296009, inch: 0 }
{ id: 3, vsm: 1968, color: 4, pmtime: 1549296014, inch: 0 }
...and so on.
The incoming data is currently limited to ten of each 'section' (vsm1, vsm2, ...vsm10, color1, color2, ...color10, etc.), so a static loop of some sort over the ten elements in each section is how i started, but seemed rather ugly and certainly not flexible.
A smart snippet that would handle n-number of elements in each section would be even better just in case the data goes beyond ten elements, or drops to just three (due to absence of data or pruned data).
I'm thinking of something along the lines of using .forEach(), but admittedly my JSON / Object manipulation skills are rather poor, so I turn to the community in the hope that someone can point me in the right direction or knows of a cool, tight routine/function that achieves what I'm looking for. Thanks in advance for any insights.
You could take an array of the wanted keys with a placeholder for the running number and build new object and push them to the result set.
var data = { vsm1: "2429", vsm2: "2488", vsm3: "1968", vsm4: "", vsm5: "", vsm6: "", vsm7: "", vsm8: "", vsm9: "", vsm10: "", color1: "5", color2: "4", color3: "4", color4: "0", color5: "0", color6: "0", color7: "0", color8: "0", color9: "0", color10: "0", p1mtime: "1549296004", p2mtime: "1549296009", p3mtime: "1549296014", p4mtime: "", p5mtime: "", p6mtime: "", p7mtime: "", p8mtime: "", p9mtime: "", p10mtime: "", inch1: "", inch2: "", inch3: "", inch4: "", inch5: "", inch6: "", inch7: "", inch8: "", inch9: "", inch10: "" },
keys = ['vsm*', 'color*', 'p*mtime', 'inch*'],
result = [],
id = 1;
while (keys[0].replace('*', id) in data) {
result.push(Object.assign(
{ id },
...keys.map(k => ({ [k.replace('*', '')]: +data[k.replace('*', id)] || 0 }))
));
id++;
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With template literals
var data = { vsm1: "2429", vsm2: "2488", vsm3: "1968", vsm4: "", vsm5: "", vsm6: "", vsm7: "", vsm8: "", vsm9: "", vsm10: "", color1: "5", color2: "4", color3: "4", color4: "0", color5: "0", color6: "0", color7: "0", color8: "0", color9: "0", color10: "0", p1mtime: "1549296004", p2mtime: "1549296009", p3mtime: "1549296014", p4mtime: "", p5mtime: "", p6mtime: "", p7mtime: "", p8mtime: "", p9mtime: "", p10mtime: "", inch1: "", inch2: "", inch3: "", inch4: "", inch5: "", inch6: "", inch7: "", inch8: "", inch9: "", inch10: "" },
templates = [id => `vsm${id}`, id => `color${id}`, id => `p${id}mtime`, id => `inch${id}`],
result = [],
id = 1;
while (templates[0](id) in data) {
result.push(Object.assign(
{ id },
...templates.map(t => ({ [t('')]: +data[t(id)] || 0 }))
));
id++;
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try this, with oldObject the object you want to clean:
var cleanedObject = {};
for (let [key, value] of Object.entries(oldObject)) {
let index = key.match('[0-9]+');
cleanedObject[index] = cleanedObject[index] || {};
cleanedObject[index][key.replace(index, '')] = value;
}
The result will be an object where cleanedObject['1'] = { vsm: 2429, color: 5, pmtime: 1549296004, inch: '' }, and so on.
This solution has a different flexibility than the one from Nina Sholz. Nina's allows you to match any style of number-containing key. But it also requires you to add a template in order to do so. Mine will handle any keys which contain only a single run of digits but nothing more complex. But it doesn't require you to do anything to handle such templates.
const reformat = data => Object.values(Object.keys(data)
.reduce(
(a, k, i, _, d = k.match(/\d+/)[0]) => ({
...a,
[d]: {...(a[d] || {id: Number(d)}), [k.replace(/\d+/, '')]: data[k]}
}), {})).sort((a, b) => a.id - b.id)
const data = {"vsm1":"2429","vsm2":"2488","vsm3":"1968","vsm4":"","vsm5":"","vsm6":"","vsm7":"","vsm8":"","vsm9":"","vsm10":"","color1":"5","color2":"4","color3":"4","color4":"0","color5":"0","color6":"0","color7":"0","color8":"0","color9":"0","color10":"0","p1mtime":"1549296004","p2mtime":"1549296009","p3mtime":"1549296014","p4mtime":"","p5mtime":"","p6mtime":"","p7mtime":"","p8mtime":"","p9mtime":"","p10mtime":"","inch1":"","inch2":"","inch3":"","inch4":"","inch5":"","inch6":"","inch7":"","inch8":"","inch9":"","inch10":""}
console.log(reformat(data))
I have no idea if you need either sort of flexibility, but these are interesting alternatives to one another.
I now see that my answer is basically the same as Ninas, haven't seen templating before so that was cool, but seeing as this i the first time i've tried to answer something here I'll just share it anyway.
As Ninas this can handle any length of data.
const data = {"vsm1": "2429",
"vsm2": "2488",
"vsm3": "1968",
"vsm4": "",
"color1": "5",
"color2": "4",
"color3": "4",
"color4": "0",
"p1mtime": "1549296004",
"p2mtime": "1549296009",
"p3mtime": "1549296014",
"p4mtime": "",
"inch1": "",
"inch2": "",
"inch3": "",
"inch4": "",
};
const vsmRegex = new RegExp("(vsm\\d)");
const keys = Object.keys(data);
const result = [];
let id= 1;
for(let i = 0; i < keys.length; i++) {
if(keys[i].match(vsmRegex)) {
let object = {
id: id,
vsm: Number(data[`vsm${id}`]) || 0,
color: Number(data[`color${id}`]) || 0,
pmtime: Number(data[`p${id}mtime`]) || 0,
inch: Number(data[`inch${id}`]) || 0
};
result.push(object);
id++;
} else {
break;
}
}
console.log(result);
I am passing a string created by JSON.stringify as a argument to another function that evals it via JSON.parse however the parse function is generating an error.
The JSON being stringified is a portion of a JSON object created by from the Xero API (the accounting package) through Service_JSON (a PHP pear package) - it validates fine.
JSON.parse seems to error on any ' characters.
Question 1: Does stringify not escape special characters like new lines and single quotes?
Question 2: Shouldn't I expect that a JSON.stringifyed string generated without error would be parsed without error in JSON.parse since they are the complimenting/opposite functions of each other?
EDIT:
Heaviliy redacted code as follows:
function PANDA_INVOICING_JS_save_invoice_add(theWindowID,loadPopupFrom) {
var theForm = $('#'+theWindowID+'_form');
var theFrameID = Date.parse(new Date());
$.ajax({
url: '?module=invoicing&action=save_invoices_add&window_uid='+theWindowID,
method: 'post',
data: theForm.serialize(),
}).done(function(result) {
var theJSON = result;
if (loadPopupFrom) {
var theURL = '?module=invoicing&action=display_invoices_details&invoice_id='+theInvoiceID+'&loadfrom='+loadPopupFrom+'&window_uid='+theFrameID;
} else {
var theURL = '?module=invoicing&action=display_invoices_details&invoice_id='+theInvoiceID;
}
/**
* XERO Allocate Credit
*/
var loadfunctions = false;
if (APP_JS_check_nestedobjects(theJSON,'data','xero_data','response')) {
if ($.isNumeric(theJSON.data.xero_data.credits.availablecredit) && theJSON.data.xero_data.credits.availablecredit > 0) {
var theXEROdata = JSON.stringify(theJSON.data.xero_data);
var loadfunctions = [
{
functionname: 'PANDA_PLUGIN_JS_xero_credit_notify',
'arguments': '####__WINDOWID__####,\''+theXEROdata+'\',\''+theURL+'\''
}
];
}
}
/* Load the new invoice */
APP_JS_frame((loadPopupFrom) ? true : false, theURL,loadfunctions);
}
});
/**
* Return true
*/
return true;
}
Passes through to this code in APP_JS_frame ( yes: the eval is a evil but necessary in this case and no user data is eval'd):
if (typeof(loadfunctions)=='object') {
for (i=0;i<loadfunctions.length;i++) {
if (! loadfunctions[i].functionname) continue; //if functionname is not defined move on
if (loadfunctions[i].arguments) {
arguments = $PHP_JS.str_replace('####__WINDOWID__####',theFrameID,loadfunctions[i].arguments)
} else {
arguments = '';
}
eval(loadfunctions[i].functionname + '('+arguments+')');
}
}
Then through to this function where the error is actually thrown:
function PANDA_PLUGIN_JS_xero_credit_notify(theFrameID, theXerodata, theURL) {
/* Parse JSON and set variables */
theXerodata = theXerodata.escapeJSON();
theXerodata = JSON.parse(theXerodata);
}
escapeJSON() is as follows:
String.prototype.escapeJSON = function() {
return this.replace(/[\\]/g, '\\\\')
.replace(/[\b]/g, '\\b')
.replace(/[\f]/g, '\\f')
.replace(/[\n]/g, '\\n')
.replace(/[\r]/g, '\\r')
.replace(/[\t]/g, '\\t')
.replace(/[']/g, "\\'");
};
As for the actual xeroData string - it should matter what it is - as JSON.stringify should generate valid JSON for JSON.parse to use - right? But for data sake here is the JSON:
{
"xero_data": {
"invoices": {
"invoice_id": "4323",
"invoice_balance": "123.00",
"date_invoice": "2016-11-20",
"contact_id": "2558",
"contact_name": "Person, Test"
},
"response": {
"return_code": 200,
"xero_response": {
"Id": "dedb737c-ce42-433f-aa2b-02478b839bbf",
"Status": "OK",
"ProviderName": "PANDA-Developer",
"DateTimeUTC": "2016-11-19T23:28:28.6858694Z",
"Invoices": {
"Invoice": {
"Contact": {
"ContactID": "b1e95873-81b2-44f6-b46a-3d4df44ef602",
"ContactStatus": "ACTIVE",
"Name": "Test Person",
"FirstName": "Test",
"LastName": "Person",
"EmailAddress": "emailaddress",
"Addresses": {
"Address": [{
"AddressType": "STREET"
}, {
"AddressType": "POBOX",
"AddressLine1": "P.O. Box",
"City": "Town",
"Region": "Otago",
"PostalCode": "1201",
"Country": "New Zealand"
}]
},
"Phones": {
"Phone": [{
"PhoneType": "DEFAULT"
}, {
"PhoneType": "DDI"
}, {
"PhoneType": "FAX"
}, {
"PhoneType": "MOBILE"
}]
},
"UpdatedDateUTC": "2016-11-19T02:49:47.58",
"IsSupplier": "false",
"IsCustomer": "true"
},
"Date": "2016-11-20T00:00:00",
"DueDate": "2016-11-30T00:00:00",
"ExpectedPaymentDate": "2016-11-30T00:00:00",
"Status": "AUTHORISED",
"LineAmountTypes": "Inclusive",
"LineItems": {
"LineItem": {
"Description": "Test Data '\n \n - new line\n - new line",
"UnitAmount": "123.00",
"TaxType": "OUTPUT2",
"TaxAmount": "16.04",
"LineAmount": "123.00",
"AccountCode": "260",
"Quantity": "1.0000",
"DiscountRate": "0.00",
"LineItemID": "a4c2da16-4d94-42d6-ae37-e3091901b79b"
}
},
"SubTotal": "106.96",
"TotalTax": "16.04",
"Total": "123.00",
"UpdatedDateUTC": "2016-11-19T23:28:28.607",
"CurrencyCode": "NZD",
"Type": "ACCREC",
"InvoiceID": "cdb5b8e9-afac-4f72-b74b-d9b292295c34",
"InvoiceNumber": "PANDA4323",
"Reference": "test 34",
"AmountDue": "123.00",
"AmountPaid": "0.00",
"SentToContact": "false",
"CurrencyRate": "1.000000",
"TotalDiscount": "0.00"
}
}
}
},
"credits": {
"0": {
"Type": "creditnote",
"UUID": "345d423e-58a6-4e05-8ae5-33da4e886bb9",
"CreditNoteNumber": "CN-0041",
"RemainingCredit": 182.65
},
"availablecredit": 182.65
}
}
}