Google Script XML Cannot Call Method of Null - google-apps-script

I have created a script which connects to an API. The script successfully parses the data and creates the required outputs. My script iterates through the record IDs to extract data for each record ID.
The existence of a child node does not exist for every parent. Thus where the parent has a child node my script operates perfectly.
var root2 = root[i].getChild('Assigned').getChildren('Staff');
However, when I encounter a parent ID where the child node does not exist I get this error. IE in the source data the client has not assigned staff to this record and thus the data node staff does not exist.
TypeError: Cannot call method "getChildren" of null.
How do I handle this error and ignore and let the code continue to run when this occurs? I have tried
if(root[i].getChild('Assigned').getChildren('Staff') != '') {
Code
}
But this still breaks the code and I get the error.

I think that the reason of the issue is that root[i].getChild('Assigned') is null, and in your script, even if Staff is not included in the parent (Assigned), no error occurs. In order to remove this issue, how about checking whether root[i] has the child of Assigned? Please try the following sample script.
Sample script:
if (root[i].getChildren().some(function(e) {return e.getName() == 'Assigned'})) {
// do something
}
In this script, it checks whether root[i] has the child of Assigned, and when the child is found, this if becomes true. So if root[i] doesn't have the child of Assigned, if becomes false, and do something is not run.
Note:
I prepared this script from the situation of your question. But I'm not sure about the actual XML data. So if this script was not what you want, can you provide the XML data or the sample data which can replicate your situation? By such sample data, I would like to modify this.
Reference:
getName()
Array.prototype.some()

Related

Using Drive.Properties.update to update or add a file property

I have enabled the Advanced Drive Service in an apps script. For a file in Google Drive, I need to set metadata properties that may already exist, so I am trying to use the update method of Drive.Properties. This method is supposed to "Update a property or add it if it doesn't exist." (See Properties: update.)
The following code fails silently (with nothing logged to the Logger and no properties added to the file). However, if I step through these same lines, I do see the catch block executed every time.
var fileId = '1jgoihblahblahblah' /* an existing file id */;
var property = {key: 'TestKey', value: 'TestValue', visibility: 'PUBLIC'}
try {
Drive.Properties.update(property, fileId, "TestKey");
} catch (e) {
Logger.log(e);
}
If I replace the call to update with this line:
Drive.Properties.insert(property, fileId);
... then the code works every time. The properties are written and are returned by calling Drive.Properties.list(fileId).
Testing the same values using the API explorer (with real values or the "TestKey" and "TestValue" examples above) always works correctly, so I don't think it's simply a matter of providing bad data. UPDATE: The API explorer is now returning an error "Property keys may only contain letters, numbers, and the characters .!#$%^&*()-_/" no matter what values I pass in. However, there are obviously no invalid characters in key names like "TestKey" and "Link" (which is one of the actual keys I am using).
1) How can I troubleshoot the error from within the Script editor? I can't yet figure out how to retrieve the error text, since update fails silently even without the try/catch.
2) I am using the syntax specified by the autocomplete prompt, since there's not actually any documentation for Apps Script syntax for advanced service.
Is that syntax correct? If so, am I doing something wrong that I'm not seeing?
This problem is irrelevant because despite its name, Drive.Properties.insert does the same thing.
See https://issuetracker.google.com/issues/36759642

SSIS - Error when passing object from one script task to another

I am passing object of a class from one script task to another. I have declared a package level variable by the name IPSService which is of type object.
The first script task contains the following piece of code
IPSService iPSService = new IPSService();
Dts.Variables["IPSService"].Value = iPSService;
I get an exception at the second line of code where the assignment happens.
The error message is as follows.
he element cannot be found in a collection. This error happens when you try to retrieve an element from a collection on a container during execution of the package and the element is not there.
Could somebody tell me what the issue is ?
You need to refer to the variable like this, notice the use of User:: in the variable name:
Dts.Variables["User::IPSService"].Value = iPSService;

Prevent Duplicate headers in flat file destination - SSIS

I need some help.
I am importing some data in .csv file from an oledb source. I don't want the headers to appear twice in the destination. If i Uncheck the "Column names in first data row" property , the headers don't get populated in the first execution as well.
Output as of now.
Col1,Col2
A,B
Col1,Col2
C,D
How can I make the package run in such a way that if the file is empty , the headers get inserted. Then if the execution happens again, headers are not included,just the data.
there was a similar thread, but wasn't able to apply the solution as how to use expressions to get the number of rows of destination itself. It was long back , so I created a new.
Your help is deeply appreciated.
-Akshay
Perhaps I'm missing something but this works for me. I am not having the read only trouble with ColumnNamesInFirstDataRow
I created a package level variable named AddHeader, type Boolean and set it to True. I added a Flat File Connection Manager, named FFCM and configured it to use a CSV output of 2 columns HeadCount (int), AddHeader (boolean). In the properties for the Connection Manager, I added an Expression for the property 'ColumnNamesInFirstDataRow' and assigned it a value of #[User::AddHeader]
I added a script task to test the size of the file. It has read/write access to the Variable AddHeader. I then used this script to determine whether the file was empty. If your definition of "empty" is that it has a header row, then I'd adjust the logic in the if check to match that length.
public void Main()
{
string path = Dts.Connections["FFCM"].ConnectionString;
System.IO.FileInfo stats = null;
try
{
stats = new System.IO.FileInfo(path);
// checking length isn't bulletproof based on how the disk is configured
// but should be good enough
// http://stackoverflow.com/questions/3750590/get-size-of-file-on-disk
if (stats != null && stats.Length != 0)
{
this.Dts.Variables["AddHeader"].Value = false;
}
}
catch
{
// no harm, no foul
}
Dts.TaskResult = (int)ScriptResults.Success;
}
I looped through twice to ensure I'd generate the append scenario
I deleted my file and ran the package and only had a header once.
The property that controls whether the column names will be included in the output file or not is ColumnNamesInFirstDataRow. This is a readonly property.
One way to achieve what you are trying to do it would be to have two data flow tasks on the control flow surface preceded by a script task. these two data flow tasks will be identical except that they will be referring to two different flat file connection managers. Again, the only difference between these two would be the different values for the ColumnsInTheFirstDataRow; one true, another false.
Use this Script task to decide whether this is the first run or subsequent runs. Persist this information and check it within the script. Either you can have a separate table for this information, or use some log table to infer it.
Following solution is worked for me.You can also try the following.
Create three variables.
IsHeaderRequired
RowCount
TargetFilePath
Get the source row counts using Execute SQL task and save it in
RowCount variable.
Have script task. Add readonly variables TargetFilePath and
RowCount. Add read and write variable IsHeaderRequired.
Edit the script and add the following line of code.
string targetFilePath = Dts.Variables["TargetFilePath"].Value.ToString();
int rowCount = (int)Dts.Variables["RowCount"].Value;
System.IO.FileInfo targetFileInfo = new System.IO.FileInfo(targetFilePath);
if (rowCount > 0)
{
if (targetFileInfo.Length == 0)
{
Dts.Variables["IsHeaderRequired"].Value = true;
}
else
{
Dts.Variables["IsHeaderRequired"].Value = false;
}
}
Dts.TaskResult = (int)ScriptResults.Success;
Connect your script component to your database
Click connection manager of flat file[i.e your target file] and go
to properties. In the expression, mention the following as shown in
the screenshot.
Map the connectionString to variable "TargetFilePath".
Map the ColumnNamesInFirstDataRow to "IsHeaderRequired".
Expression for Flat file connection Manager.
Final package[screenshot]:
Hope this helps
A solution ....
First, add an SSIS integer variable in the scope of the Foreach Loop or higher - I'll call this RowCount - and make its default value negative (this is important!). Next, add a Row Count to your Data Flow, and assign the result to the RowCount SSIS variable we just made. Third, select your Connection Manager (don't double-click) and open the Properties window (F4). Find the Expressions property, select it, and hit the ellipsis (...) button. Select the ColumnNamesInFirstDataRow property, and use an expression like this:
[#User::RowCount] < 0
Now, when your package starts, RowCount has the static value of -1 or another negative number. When the data flow starts for the first time in your loop, the ColumnNamesInFirstDataRow property will have a value of TRUE. When the first data flow completes, the row count (even if it's zero) is written to the RowCount variable. On the second interation of the loop, the Connection Manager is then reconfigured to NOT write column names...

getTag() method not properly working for google apps script

What i am doing in writing a script that lets the User interact with a data table. Every series that the user chooses creates a button, and then plots the series on a graph. if the user click the button it rooms the series. All there the data is stored in a hidden JSON string. the columns, or series that the user whats to see are stored in an array that i call index, it is also a hidden JSON string) Each button is connected to its own client handler, which has a
.forTargets(the index i was talking about).setTag(to the corresponding column in the data array)
and they are all connected to the same server handler. So when the button is clicked the client handler sets the tag for the index to the series that it is supposed to be removed. Now the server handler will run it get the index by ID and get the Tag. This is were it goes wrong. The tag is always null.
The first thing i tried was to see if my client handler was not working properly. So i set the tag of the index to some number, but the getTag method in the Server handler still returned null.
idk, but maybe Tags are not saved in the UI instance??
Below is the first bit of the Server handler.
function clickServer(e) {
e = e.parameter;
var app = UiApp.getActiveApplication();
var master = JSON.parse(e.dataTable, "reviver");
var index = JSON.parse(e.index, "reviver");
var hidden = app.getElementById("hiddenIndex");
var tag = hidden.getTag();
I think the issue you are meeting is more related to timing : handlers are called simultaneously, this is true for client an server handlers as well, that means that if the client handler changes a hidden tag value this change happens too late for the server handler function to 'see' it. What you should do is create a separate button to trigger the server handler that the user would use after all the other parameters where set.
This very same method is used in the known workaround used to get radioButtons value
Also, why do you use tags on the hidden widget ? you could use it with direct access by setValue() and e.parameter.hiddenName since they are already invisible by nature... ?
Note also that you can set a value in client handlers as long a these values are defined inside the Ui instance (the do Get function) either by constant variables or by another client Handler in the same function, as shown in the before mentioned example with radioButtons... but that's only a detail.
In this context if you need to get a widget value (inside the doGet function) you should of course use getValue() to get the widget value as no e.parameter is available at this time.
I hope I've been clear enough, re-reading this I'm not very sure but.... just ask if something is missing ;-)
The tags values are passed to handlers via parameters. In this post this fact is explained in details.

Coldfuson CFScript "setSQL" method was not found

I've got a Coldfusion component, with a method in it called getColumnNames
This just queries a MySQL table, and returns the columnlist:
remote string function getColumnNames() {
qProcessCars = new Query();
qProcessCars.setDataSource('#APPLICATION.dsn#');
qProcessCars.setSQL('SELECT * FROM sand_cars WHERE 1 LIMIT 1');
qProcessCars = qProcessCars.Execute().getResult();
return qProcessCars.columnlist;
}
If I access this remotely in the browser, with page.cfc?method=getColumnNames, then I get the expected list of columns back.
However, if I try to access this from inside another method within the component, I get an error
remote string function otherFunction() {
...
sColumns = getColumnNames();
...
}
The error dump for the above code returns the message "The setSQL method was not found".
So can anyone help me find out why it works as a remote call, but not when called from another method inside the same component.
Problem may be caused some kind of race conditions. If you make few calls which interfere, at some point qProcessCars may already be query result, so invoking method is not possible.
I would try to make the qProcessCars variable local scoped (var qProcessCars = new Query();
) and/or try to use another variable name for query results.
Next possible step is to enclose the query building/executing code into named lock.
Ah I've answered my own question again. Sorry.
I've used the same name qProcessCars else where in the component, I hadn't put var in front of them.
I don't know WHY that was causing the problem, but it was. Maybe setSQL can only be called once per query object?