I am working on a Google Data Studio community connector, and I am trying to display only one field to start with. All of the code is in the code.gs file, except getAuthType() and isAdminUser() is in Auth.gs.
The issue is that once getData() is called, getFields() returns an empty object. Here is the log:
It looks like getSchema() is working correctly, here is the log from that function:
Here is the getFields() function:
function getFields() {
var fields = cc.getFields();
var types = cc.FieldType;
fields
.newDimension()
.setId('device')
.setName('Device')
.setType(types.TEXT);
return fields;
}
Here is getData():
function getData(request){
console.log('getData request', request);
request.configParams = validateConfig(request.configParams);
console.log('getFields()', getFields(), typeof getFields());
var requestedFields = getFields().forIds(
request.fields.map(function(field){
return field.name;
})
);
try{
console.log('start request');
var apiResponse = fetchDataFromApi(request);
var data = formatData(apiResponse, requestedFields);
}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.'
)
.throwException();
}
return {
schema: requestedFields.build(),
rows: data,
}
}
getSchema():
function getSchema(request){
var schema = {schema: getFields().build()};
console.log(schema);
return schema;
}
The community connector is initialized outside of all functions in the same file:
var cc = DataStudioApp.createCommunityConnector();
I looked through the documentation, but I cannot find anything that would cause getFields() to return an empty object, based off what I have. Any thoughts?
I thought that getFields() returning an empty object was causing my app to display the fields incorrectly, but the issue was that the data I was passing to the app was in the incorrect format.
getFields() isn't useful until it is called with build() (i.e. getFields().build() )
I think it is expected behavior to receive an empty object for getFields() alone.
Related
Im writing a function to export data pulled from C# to a csv file in JavaScript. The data is trying to be passed into an active webpage with live data. The goal is to export said data from C# string to the clients window hence the transfer to JavaScript (if my understanding is right). Then the JavaScript function in the page will download to the users window.
My issue is why am I have the invalid export data, please provide an array of objects output when I think I provide it one?
C# Code:
protected string test()
{
MyRequest request = new MyRequest();
request.Id = "TEST";
// Cant post this class but this is what I do
var tmp = new ExistingClassOfMine();
// pulls the data from the existing class through GetResponse();
// Then convert to json string
string data = JsonConvert.SerializeObject(tmp.GetResponse(request));
// Data found from breakpoint: {"Slot1":[{"Name":"TJ", "ID":"123"},{"Name":"Joe","ID:456"}], "TotalCount":2}
return data;
}
JavaScript:
function exportToExcel() {
console.log("Exporting to Excel");
isExporting = true;
const fileName = 'NameList';
const exportType = 'csv';
var data = <%=this.test()%>;
var readData = data["Slot1"];
var myArray = [];
// Followed a stack article to create this
for (var i in readData) {
myArray.push(readData[i]);
}
// Logging stuff for debugging
console.log("Data from C#");
console.log(data);
console.log(typeof (data));
console.log("Data we want to export");
console.log(readData);
console.log("Parsed Data?");
console.log(myArray);
// fails here with the error Invalid export data, please provide an array of objects
// which I thought I did with my for loop up a few lines
window.exportFromJSON({myArray, fileName, exportType});
isExporting = false;
}
function onRequestStart(sender, args) {
if (isExporting) {
args.set_enableAjax(false);
}
}
Log output:
Look, your triying to change the name of the key which is sent to exportFromJSON. The right way to name the key is data no myArray.
Change:
myArray
to
data
or assign myArray to data key
window.exportFromJSON({data:myArray, fileName, exportType});
I am trying to read "OrderedTime" or any other field from the below structure(Image) but i always get output as "undefined" , but when i use "Amount" field then i am getting output as "Total Amount : 10₹" , I am not sure what i am doing wrong here.
exports.addCfTimeTodeliverables = functions.database.ref('/root/{VendorId}/deliverables/{key}').onCreate((snap, context)=>{
const data = context.params.VendorId
var requestSnapshot = snap.val().PhoneNumber; // getting undefined for all the fields (Not working as expected)
var requestSnapshot = snap.val().Amount; // getting "Total Amount : 10₹" (Working as expected)
console.log("------->"+requestSnapshot);
})
Could anyone help me to resolve this issue?
Screenshot of firebase structure
Our function must always return something, so we can solve this error by adding
return true;
after the last line.
From this:
exports.addCfTimeTodeliverables = functions.database.ref('/root/{VendorId}/deliverables/{key}').onCreate((snap, context)=>{
const data = context.params.VendorId
var requestSnapshot = snap.val().PhoneNumber; // getting undefined for all the fields (Not working as expected)
var requestSnapshot = snap.val().Amount; // getting "Total Amount : 10₹" (Working as expected)
console.log("------->"+requestSnapshot);
})
To this:
exports.addCfTimeTodeliverables = functions.database.ref('/root/{VendorId}/deliverables/{key}').onCreate((snap, context)=>{
const data = context.params.VendorId
var requestSnapshot = snap.val().PhoneNumber; // getting undefined for all the fields (Not working as expected)
var requestSnapshot = snap.val().Amount; // getting "Total Amount : 10₹" (Working as expected)
console.log("------->"+requestSnapshot);
return true;
})
I created a sample database with the same structure as yours and I got the value for PhoneNumber. What I noticed is that after getting that value I got the data as follows:
-------> 1111111111
{"severity":"WARNING","message":"Function returned undefined, expected Promise or value"}
Which I suspect is the message you're getting but in any case the data in being retrieved and the other message is only a warning. Anyway, the code I used is as follows, give it a try:
exports.addCfTimeTodeliverables = functions.database
.ref('/root/{VendorId}/deliverables/{key}')
.onCreate((snap, context) => {
const data = context.params.VendorId;
var requestSnapshot = snap.val().PhoneNumber;
console.log("------->"+requestSnapshot);
return null;
});
I have an onCall function that looks like this..
exports.getResults = functions.https.onCall((data, context) => {
const admin = context.auth.token.admin
const uniqueId = context.auth.uid
const doc = db.collection('collection').doc(data.docId)
return doc
.get()
.then(querySnapshot => {
return querySnapshot.docs.map(doc => {
const doc = doc.data()
const collectionDoc = {
docValues
}
return collectionDoc
})
})
my console.log prints the doc I am requesting as an object, so I'm not sure what the issue is?
I understand that you have this error in Firebase Function logs. This means that you are returning NaN somewhere in the object. According to this article:
To send data back to the client, return data that can be JSON encoded.
It may depend on implementation, however here NaN is cannot be JSON encoded. (Found something about it in Wikipedia - Data types and syntax - Number).
You can easily replicate this problem by deploying function with not initialized variable:
exports.getResults = functions.https.onCall(() => {
var number;
var value = number+1;
return {
value,
"text": "test",
};
})
In the example I added undefined to number the result will be NaN. Of course there might be other functions that will return NaN as well. Anyway if you deploy example above, it will log the same error "Data cannot be encoded in JSON. NaN". I think this is most easily to be overlooked in the code.
I think you have to carefully check all data that you are returning.
i'm currently working with rendering some graphs on a web mvc project. The graphs already render on my machine when i'm debugging the code, but the moment I publish it on the IIS of my QA server, I get the following error on console
TypeError: JSON.parse(...).forEach is not a function
Here's the a snippet of the code I'm currently working
ajaxPostConstancy.done(function (html) {
Utils.Alerts.HideGif();
var data = {};
var category = [];
var colors = [];
JSON.parse(html).forEach(function (e) {
category .push(e.date);
colors.push(e.color);
data[e.date] = e.data1;
})
....
any ideas of what's going on?
Edit: the html var inside the JSON.parse is te string returned by this code
public async Task<string> GetCompositionGraph(string contract, string methodName)
{
string preFormat = null;
try
{
string method = _configuration["Position:method:" + methodName];
PositionBL _bl = new PositionBL(Request, Response, _baseUri, method);
object model = await _bl.PostCompositionGraph(contract);
preFormat = JsonConvert.SerializeObject(model);
}
catch(Exception ex)
{
ViewBag.error = ex.Message;
}
return preFormat;
}
edit 2: the content of html variable which is generated by the code on the first edit:
html content:
[{"color":"#162ECB","date":"20","data1":1122954.8708},{"color":"#E03473","date":"00","data1":1323061.6168},{"color":"#CE029D","date":"26","data1":29982.2271}]
and this picture is the result I get from the JSON.parse when I debug my website
Edit 3: Visual input
The explorer console when the sites is deployed on my localhost for debugging
The explorer console while checking the site published on QA server
Edit 4: So i narrowed it down to the fact that the error comes when I debug the websit in Release mode, so that's kind of a step foward
If preformat returns null or an object forEach() will throw an error.
ajaxPostConstancy.done(function (html) {
Utils.Alerts.HideGif();
var data = {};
var category = [];
var colors = [];
var parsedDatas = JSON.parse(html)
if (Array.isArray(parsedDatas)) {
parsedDatas.forEach(function (e) {
// logic here
});
} else {
console.warn('This is not an array : ', html)
}
...
The forEach() method calls a function once for each element in an array while the JSON.parse() makes the html an object.
Preface: I'm new to JavaScript. If this question is startlingly stupid, that's (part of) the reason.
Begin Update
I found the answer to my question prior to the flag as a dupe. I solved it with a try-catch block. This answer does not reference a try-catch block.
How do I return the response from an asynchronous call?
End Update
I'm attempting to create an Alexa project from scratch (well, at least without one of Amazon's templates). I've written the "guts" of the app and I've tested my functions with chai. Things were going swimmingly until I tried to wire up some intents.
I can see my intents are being sent based on console.log statements I've thrown in the helperClass, but the return values aren't making it back to my index.js file.
Two questions:
What am I mucking up?
How do I fix it?
Here's what I've done:
Based on that, I dug around to see what's going on in my index.js file's headers and I saw this:
var Alexa = require('alexa-app');
So I went to alexa-app and saw that it uses bluebird, which suggests to me that I'm dealing with a promise problem. Further, I saw this in the log when I send a request that works:
preRequest fired
postRequest fired
When a request doesn't work, I only see:
preRequest fired
I'm using Big Nerd Ranch's "Developing Alexa Skills Locally with Node.js".
Here's my problematic intent in my index.js file:
app.intent('getDaysFromNow', {
'slots': {
'INPUTDATE' : 'AMAZON.DATE'
},
'utterances': ['{|number of|how many} {days} {until|from|since} {|now|today} {|is|was} {-|INPUTDATE}'] // almost perfect
},
function(req, res) {
console.log('app.intent getDaysFromNow fired');
//get the slot
var inputDate = req.slot('INPUTDATE');
var reprompt = 'Ask me how many days until or from a specified date.';
if (_.isEmpty(inputDate)) {
console.console.log('app.intent daysFromNow blank request');
var prompt = 'I didn\'t hear the date you want.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
console.log('getDaysFromNow slot is not empty.');
var dateHelper = new DateHelper();
dateHelper.getDaysFromNow(inputDate).then(function(daysFromNow) {
console.log(daysFromNow);
res.say(dateHelper.formatDaysFromNowResponse(daysFromNow)).send(); // FIXME
}).catch(function(err) {
console.log(err.statusCode);
var prompt = 'Hmm...I don\'t have a date for that ' + inputDate;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
});
return false;
}
}
);
I know it's getting sent, but the value's not getting back to index.js. I think I've got a return problem. Here's the function in my helperClass.js whose return isn't getting back to index.js
// Takes AMAZON.DATE string as its argument
DateHelper.prototype.getDaysFromNow = function(inputDate) {
if (isValidDate(inputDate)) { // validate it
// if it's valid, run the function
inputDate = moment(inputDate, "YYYY-MM-DD").startOf('day'); // inputDate starts as a string, recast as a moment here
// create currentDate moment from current Date()
var currentDate = moment(new Date()).startOf('day');
// Calculate daysFromNow here
var daysFromNow = inputDate.diff(currentDate, 'days');
console.log("\t" + 'daysFromNow = ' + daysFromNow);
// ORIGINAL CODE
// return daysFromNow;
// EXPERIMENTAL CODE
return this.daysFromNow.then(
function(response) {
return response.body;
}
);
} else {
// throw an error
throw new Error("getDaysFromNow(): argument must be valid AMAZON.DATE string");
}
};
Thank you for reading. I welcome your suggestions.
It turns out it's not the way the return is sent from helperClass. The issue is how the call was made in the first place.
For the helperClass, I reverted to the original return.
// This is the return from `helperClass`
return daysFromNow;
In the index.js class, I put the call to helperClass in a try-catch block, like so:
app.intent('getDaysFromNow', {
'slots': {
'INPUTDATE': 'AMAZON.DATE'
},
'utterances': ['{|number of|how many} {days} {until|from|since} {|now|today} {|is|was} {-|INPUTDATE}'] // almost perfect
},
function(req, res) {
console.log('app.intent getDaysFromNow fired');
//get the slot
var inputDate = req.slot('INPUTDATE');
var reprompt = 'Ask me how many days until or from a specified date.';
if (_.isEmpty(inputDate)) {
console.log('app.intent daysFromNow blank request');
var prompt = 'I didn\'t hear the date you want.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
// ** CHANGED: This is the part that changed. **
console.log('getDaysFromNow slot is not empty.');
var dateHelper = new DateHelper();
try {
var daysFromNow = dateHelper.getDaysFromNow(inputDate);
res.say(dateHelper.formatDaysFromNowResponse(daysFromNow)).send();
} catch (error) {
console.log("error", error);
var prompt = 'Hmm...I don\'t have a date for that ' + inputDate;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
}
return false;
}
}
);
Hopefully, someone finds this helpful if they run into the same problem.