Selecting a specific node in JSON using Appcelerator Titanium - json

Hopefully an easy question for you.
I have an API service from Rackspace which I call, recently they changed the order of some of the responses, meaning part of my app would no longer upload images to the correct POST url.
I've put a temp fix in place by hard coding the URL's it should be posting to, but I want to be sure I future proof against any changes in their API ordering or indeed any changes to the URL itself.
So, originally I was using the JSON response and choosing the first node and its children.
var CFtenantID = getRSToken.access.serviceCatalog[0].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[0].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[0].endpoints[0].publicURL;
Now, that node has moved to position 18 in a long list. So, I can manually set the script to retrieve it.
var CFtenantID = getRSToken.access.serviceCatalog[17].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[17].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[17].endpoints[0].publicURL;
What I'd like to be able to do, is scan for the "name" instead and just return the CloudFiles info I need, rather than having to declare an actual number of the array.
Here is a snippet of the JSON response from Rackspace.
{
"name": "cloudFeeds",
"endpoints": [
{
"region": "LON",
"tenantId": "1234",
"publicURL": "https://lon.feeds.api.rackspacecloud.com/1234",
"internalURL": "https://atom.prod.lon3.us.ci.rackspace.net/1234"
}
],
"type": "rax:feeds"
},
{
"name": "cloudFiles",
"endpoints": [
{
"region": "LON",
"tenantId": "MossoCloudFS_xxxxxx",
"publicURL": "https://storage101.lon3.clouddrive.com/v1/MossoCloudFS_xxxxx",
"internalURL": "https://snet-storage101.lon3.clouddrive.com/v1/MossoCloudFS_xxxxx"
}
],
"type": "object-store"
},
{
"name": "cloudFilesCDN",
"endpoints": [
{
"region": "LON",
"tenantId": "MossoCloudFS_xxxxxx",
"publicURL": "https://cdn3.clouddrive.com/v1/MossoCloudFS_xxxxxx"
}
],
"type": "rax:object-cdn"
}
And here is my overall script in Appcelerator.
var authCloudFiles = Ti.Network.createHTTPClient({
onload: function() {
// output
Ti.API.info(this.responseText);
var getRSToken = JSON.parse(this.responseText);
var rsToken = getRSToken.access.token.id;
var rsTenantID = getRSToken.access.token.tenant.id;
var rsTenantName = getRSToken.access.token.tenant.name;
var CFtenantID = getRSToken.access.serviceCatalog[17].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[17].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[17].endpoints[0].publicURL;
var rsUserID = getRSToken.access.user.id;
Ti.App.Properties.setString('rsToken', rsToken);
Ti.App.Properties.setString('rsTenantID', rsTenantID);
Ti.App.Properties.setString('rsTenantName', rsTenantName);
Ti.App.Properties.setString('CFtenantID', CFtenantID);
Ti.App.Properties.setString('CFregion', CFregion);
Ti.App.Properties.setString('CFpublicURL', CFpublicURL);
Ti.App.Properties.setString('rsUserID', rsUserID);
//alert(rsToken);
Ti.API.info('rsToken: ' + rsToken);
Ti.API.info('rsTenantID: ' + rsTenantID);
Ti.API.info('rsTenantName: ' + rsTenantName);
Ti.API.info('CFtenantID: ' + CFtenantID);
Ti.API.info('CFregion: ' + CFregion);
Ti.API.info('CFpublicURL: ' + CFpublicURL);
Ti.API.info('rsUserID: ' + rsUserID);
// then we need to load the next step
rackspaceUpload();
}
});
authCloudFiles.open('POST', 'https://identity.api.rackspacecloud.com/v2.0/tokens');
Can anyone help?
Simon

I have never used Appcelerator Titanium before, but it looks like it is just JavaScript.
You could try replacing the following:
var CFtenantID = getRSToken.access.serviceCatalog[17].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[17].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[17].endpoints[0].publicURL;
with:
var CFtenantID = "";
var CFregion = "";
var CFpublicURL = "";
var catalog = getRSToken.access.serviceCatalog;
for (var i=0; i<catalog.length; i++) {
if (catalog[i].name == "cloudFiles") {
for (var j=0; i<catalog[i].endpoints.length; j++) {
if (catalog[i].endpoints[j].region == "LON") {
CFtenantID = catalog[i].endpoints[j].tenantId;
CFregion = catalog[i].endpoints[j].region;
CFpublicURL = catalog[i].endpoints[j].publicURL;
break;
}
}
}
}
My JavaScript is a little rusty though, you may want to add some additional error handling.

Related

Getting values from JSON using appscript

I'm creating two-dimentional array with google appscript using chrome devices users with the JSON response from a directory. I've been able to get values. However, when I try to get recentUsers represented as:
{
"kind": "directory#chromeosdevices",
"chromeosdevices": [
{
"kind": "directory#chromeosdevice",
"etag": "1234567890"
"deviceId": "def456",
"serialNumber": "234567",
"status": "ACTIVE",
"lastSync": "2013-03-05T17:30:04.325Z",
"supportEndDate": "2014-04-05T17:30:04.325Z",
"annotatedUser": "help desk",
"annotatedLocation": "Mountain View help desk Chromebook",
"annotatedAssetId": "1234567890",
"notes": "Loaned from support",
"orderNumber": "1234",
"willAutoRenew": true,
"osVersion": "Browser Version 18.0",
"platformVersion": "Platform Version 1415.2.0",
"firmwareVersion": "Firmware Version 1.2.3.4",
"bootMode": "validated",
"lastEnrollmentTime": "2012-04-05T17:30:04.325Z",
"orgUnitPath": "corp/engineering",
"recentUsers": [
{
"type": "USER_TYPE_MANAGED",
"email": "user#customer.com" //I'm trying to get the most recent user's email
}
],
"activeTimeRanges": [
{
"date": "2012-04-05",
"activeTime": "3600000"
}
],
}
],
"nextPageToken": "abcdefghijkl123"
}
I get an array but I can't get the value. I'm trying to get the most recent user's email. Anyone have experience with appscript? I've tried recentUsers[0].email and recentUser[0]['email']. This is the code I have to far: //I commented where I'm trying to push a value
enter code here
var chromeArgs = {
maxResults: 200,
orderBy: 'annotatedUser',
projection: "FULL"
};
var getChromes = (AdminDirectory.Chromeosdevices.list('XXXXXXXXX', chromeArgs));
var chromes = getChromes.chromeosdevices;
if (chromes && chromes.length > 0) {
Logger.log('Devices:');
//2d array
var wholeValues = [];
for (var i = 0; i < chromes.length; i++){
//create a 1D array first with pushing 0,1,2 elements with a for loop
var value = [];
for (var j = 0; j < 1; j++) {
var chrms = chromes[i];
var recentUser = chrms.recentUsers;
value.push(recentUser[0].email); // Here is where I need help
}
//pushing the value array with [0,1,2] to thw wholeValues array.
wholeValues.push(value);
} // the outer for loop runs five times , so five the 0,1,2 with be pushed in to thewholevalues array by creating wholeValues[0][0],wholeValues[0][1]...till..wholeValues[4][2]
Logger.log(wholeValues); '''
You are very close, the only thing you need to change is to replace the static recentUsers[0] with dynamic recentUsers[j] within the loop
Sample:
function getChromes(){
var chromeArgs = {
maxResults: 200,
orderBy: 'annotatedUser',
projection: "FULL"
};
var getChromes = (AdminDirectory.Chromeosdevices.list('XXXXXXXXX', chromeArgs));
var chromes = getChromes.chromeosdevices;
if (chromes && chromes.length > 0) {
Logger.log('Devices:');
var wholeValues = [];
for (var i = 0; i < chromes.length; i++){
var chrms = chromes[i];
var recentUser = chrms.recentUsers;
var value = [];
for (var j = 0; j < recentUser.length; j++) {
Logger.log(recentUser[j].email);
value.push(recentUser[j].email);
}
wholeValues.push(value);
}
Logger.log(wholeValues);
}
}
Note that you need to specify the iteration limit of the inner loop dynamically j < recentUser.length; instead of j < 1 to account for different quantity of recent users for each chrome device.

Cannot return a card markup from the callback function of a universal action

I'm just trying to get started with the sample addon Google describes here for extending the compose UI:
https://developers.google.com/gsuite/add-ons/gmail/extending-compose-ui
However when I run it I'm getting this error:
Error with the add-on. Run time error. Cannot return a card markup
from the callback function of a universal action.
I have not setup any universal actions in my manifest file:
{
"timeZone": "America/New_York",
"oauthScopes":[
"https://www.googleapis.com/auth/script.send_mail",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata",
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/gmail.addons.current.action.compose"
],
"gmail":{
"name": "My Mail Merge",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/label_googblue_24dp.png",
"composeTrigger": {
"draftAccess": "METADATA",
"selectActions": [
{
"text": "show UI",
"runFunction": "buildImageComposeCard"
}
]
},
"openLinkUrlPrefixes": [
"https://mail.google.com/"
],
"primaryColor": "#42585F4",
"secondaryColor": "#42585F4"
},
"exceptionLogging": "STACKDRIVER"
}
Below is Code.gs:
function getInsertImageComposeUI(e) {
return [buildImageComposeCard()];
}
function buildImageComposeCard() {
// Get a list of image URLs to display in the UI.
// This function is not shown in this example.
var imageUrls = [
"https://mail.google.com/1",
"https://mail.google.com/2",
"https://mail.google.com/3"
];
var card = CardService.newCardBuilder();
var cardSection = CardService.newCardSection().setHeader('My Images');
for (var i = 0; i < imageUrls.length; i++) {
var imageUrl = imageUrls[i];
cardSection.addWidget(
CardService.newImage()
.setImageUrl(imageUrl)
.setOnClickAction(CardService.newAction()
.setFunctionName('applyInsertImageAction')
.setParameters({'url' : imageUrl})));
}
return card.addSection(cardSection).build();
}
function applyInsertImageAction(e) {
var imageUrl = e.parameters.url;
var imageHtmlContent = '<img style=\"display: block\" src=\"'
+ imageUrl + '\"/>';
var response = CardService.newUpdateDraftActionResponseBuilder()
.setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()
.addUpdateContent(
imageHtmlContent,
CardService.ContentType.HTML)
.setUpdateType(
CardService.UpdateDraftBodyType.IN_PLACE_INSERT))
.build();
return response;
}
In the manifest file, runFunction has to be set to "getInsertImageComposeUI",
Also, in the code file, CardService.ContentType.HTML has to be one of
CardService.ContentType.MUTABLE_HTML
or
CardService.ContentType.IMMUTABLE_HTML
I have the same error message and couldn't get that example to work. However, I created a simpler Hello-World example that actually works. I think it is more useful for newbies to start with. Don't forget to set the runFunction in the manifest to getComposeCard.
function getComposeCard(e) { return [buildComposeCard()]; }
function buildComposeCard() {
var card = CardService.newCardBuilder();
var cardSection = CardService.newCardSection().setHeader('Test Header');
testVar = ['item1', 'item2'];
for (var i = 0; i < testVar.length; i++) {
var item = testVar[i];
cardSection.addWidget(
CardService.newTextParagraph().setText(item));
}
return card.addSection(cardSection).build();
}
Probably it's way too late, but the problem can be fixed by simply wrap the result of buildImageComposeCard as array
return [card.addSection(cardSection).build()]

How to set one more level in Object of a not predefined item?

The main issue is the following:
Get JSON from the server
Put data to form
Serialize form
Create JSON with correct structure
Send it to the server
I have difficulties on the fourth step, what I've done:
methods: {
onSubmit: function() {
var dataForm = new FormData(this.$refs['form']);
var data = [];
for (var _iterator = dataForm.entries(), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _Object$assign;
var _Helper = {};
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var _ref2 = _ref,
key = _ref2[0],
val = _ref2[1];
Object.assign(_Helper, (_Object$assign = {}, _Object$assign[key] = val, _Object$assign));
}
}
},
Here you go - a link to the codepen.
As you can see I could create JSON like that:
{"0":"a","1":"b","2":"c","3":"d","4":"e","5":"d"}
However, I need smth like that:
{
"0": {
"text": "a"
},
"1": {
"text": "b"
},
"2": {
"text": "c"
},
"3": {
"text": "d"
},
"4": {
"text": "e"
},
"5": {
"text": "d"
}
}
What Can I do to implement it and also keep the correct structure of my JSON?
To change the format, change in the location it assigns the property value:
Object.assign(data, (_Object$assign = {}, _Object$assign[key] = {text: val}, _Object$assign));
// -------------------------------------------------------------^^^^^^^^^^^
Instead of a string (val), assign the object in the format you want ({text: val}).
Updated CodePen.
If you can use modern (ES6) JavaScript, there's a much shorter notation for that:
var [key, val] = _ref;
Object.assign(data, {[key]: {text: val}});
CodePen here.
Or (because you are using FormData#entries() you do are using modern JS):
var formData = Array.from(new FormData(this.$refs['form']).entries());
var data = Object.assign(...formData.map(([key, val]) => ({[key]: {text: val}})));
console.log(JSON.stringify(data));
Targetting IE10 and above
To target IE10 and above you'll need polyfills. The problem is not the syntax, but the absence of functions like .entries(), that will be added by the polyfills. To use the least amount possible of polyfills, you'll have to iterate the iterator "manually" (kind of like you are already). For more info, check this answer.
You can do the whole construction much more simply:
onSubmit: function () {
const dataForm = new FormData(this.$refs['form']);
const data = {};
for (const i of dataForm.entries()) {
data[i[0]] = { text: i[1] }
}
console.log(JSON.stringify(data));
}

Delete token from SAPUI5 Multi Input Field with Data Binding

in my SAPUI5 app I am using a Multi Input Field with tokens which are bound to a JSON Model. Newly added entries are saved in the JSON Model. However, when deleting a token by pressing the "x" next to the token text, the token disappears from the multi input field. But when adding a new token the deleted one reappears.
How can I ensure that the deleted entry is also deleted from the JSON Model?
This is my current code for adding the token to the model:
multiInputField.addValidator(function(args){
MessageBox.confirm("Do you really want to add Token\"" + args.text + "\"?", {
onClose: function(oAction) {
if (oAction === MessageBox.Action.OK){
var oToken = new Token({key: args.text, text: args.text});
args.asyncCallback(oToken);
var aFields = sap.ui.getCore().getView().getModel("myModel").getProperty("/Tokens");
var oNewFields= {
Tokens: args.text
};
aFields .push(oNewFields);
sap.ui.getCore().getView().getModel("myModel").setProperty("/Tokens", aFields );
sap.ui.getCore().getView().getModel("myModel").refresh();
} else {
args.asyncCallback(null);
}
},
title: "Add Token"
});
return sap.m.MultiInput.WaitForAsyncValidation;
});
I guess we can use "tokenUpdate" event for this.
For example, given that I have this MultiInput in my view:
<MultiInput width="500px" id="multiInput" suggestionItems="{ path: 'dataModel>/data'}" showValueHelp="true" tokenUpdate="onTokenUpdate">
<core:Item key="{dataModel>key}" text="{dataModel>value}"/>
</MultiInput>
then in my controller I can handle this like :
onTokenUpdate: function(oEvent) {
var sType = oEvent.getParameter("type");
if (sType === "removed") {
var sKey = oEvent.getParameter("removedTokens")[0].getProperty("key");
var oModel = this.getView().getModel("dataModel");
var aData = this.getView().getModel("dataModel").getProperty("/data");
for (var i = 0, len = aData.length; i < len; i++) {
var idx;
console.log(sKey + "-" + aData[i].key);
if (aData[i].key === sKey) {
idx = i;
}
}
aData.splice(idx, 1);
oModel.setProperty("/data", aData);
console.log(oModel);
}
}
And this is my json:
{
"data": [
{
"key": "token1",
"value": "token1"
},
{
"key": "token2",
"value": "token2"
}
]
}

jQuery and Json push issue

I am okay with using push({}) to create my javascript array, however one of my fields has a list of its own. So I'm having trouble doing the push() on level down.
As follows, tradeDetails array is defined and the EXTENSIONS field has a collection of values. How do I handle this one below.
function TradeDetailsToJson(xml) {
var tradeDetails = [];
var tradeId = $(xml).find("tradeHeader tradeId").text();
var tradeDate = $(xml).find("tradeHeader tradeDate").text();
var tradeType = $(xml).find("tradeHeader tradeType").text();
var counterparty = $(xml).find("tradeHeader counterparty").text();
var internalUnit = $(xml).find("tradeHeader internalUnit").text();
var buySell = $(xml).find("tradeHeader buySell").text();
var status = $(xml).find("tradeHeader status").text();
tradeDetails.push({
"tradeId": tradeId,
"tradeDate": tradeDate,
"tradeType": tradeType,
"counterparty": counterparty,
"internalUnit": internalUnit,
"buySell": buySell,
"status": status,
"**extensions**":[]
});
// NOW I'D LIKE TO PUSH MY EXTENSIONS !!
var extName = ""
var extValue = "";
$(xml).find("extensions extension").each(function () {
extName = $(this).find("name").text();
extValue = $(this).find("value").text();
//tradeDetails[extensions].push({ "name": name, "value": value }); ??????
});
}
thank you in advanced.
Bob
Use push on the extensions property again?
tradeDetails.push({
"tradeId": tradeId,
"tradeDate": tradeDate,
"tradeType": tradeType,
"counterparty": counterparty,
"internalUnit": internalUnit,
"buySell": buySell,
"status": status,
"extensions":[]
});
tradeDetails.extensions.push({ "name": name, "value": value });
With what you have tried, you need to wrap extensions which is the key of the object tradeDetails, in quotes.
So change
tradeDetails[extensions].push({ "name": name, "value": value });
to
tradeDetails["extensions"].push({ "name": name, "value": value });