I'm using the modern SOAP endpoint and Sdk.Soap.js and Sdk.ExecuteMultiple.js to execute a bunch of create requests from Javascript. Everything works fine, except when I get the response items back, it logs out the same id for each response item. I can't figure out how to get the ids out of the response item. At the bottom, where I log out the ids, I get the same id logged out over and over again. Yet all of the records get created and they all have unique ids. Not sure how to get all of the response ids out. Here is my code:
createOpportunities = function(data) {
var createRequest = new Sdk.Collection(Sdk.OrganizationRequest);
var requestSettings = new Sdk.ExecuteMultipleSettings(true,true);
var pricelevel = {
'id': $("#season").find(':selected').data('pricelevelid'),
'name': $("#season").find(':selected').text()
};
var stage = {
'id': $("#stage").find(':selected').data('stageid'),
'name': $("#stage").find(':selected').text()
};
var product = {
'id': $("#product").find(':selected').data('productid'),
'name': $("#product").find(':selected').text()
};
var source = {
'id': $("#source").find(':selected').data('sourceid'),
'name': $("#source").find(':selected').text()
};
data.forEach(function(d,i){
var Opportunity = new Sdk.jms_opportunity();
Opportunity.JMS_ContactId.setValue(new Sdk.EntityReference('contact', d.contactid));
Opportunity.OwnerId.setValue(new Sdk.EntityReference('systemuser', d.ownerid.getId()));
Opportunity.JMS_pricelevelid.setValue(new Sdk.EntityReference('JMS_pricelevel', pricelevel.id));
Opportunity.JMS_stageid.setValue(new Sdk.EntityReference('str_ticketstage', stage.id));
Opportunity.JMS_ProductId.setValue(new Sdk.EntityReference('JMS_product', product.id));
Opportunity.JMS_sourceid.setValue(new Sdk.EntityReference('jms_source', source.id));
createRequest.add(new Sdk.CreateRequest(Opportunity));
});
var request = new Sdk.ExecuteMultipleRequest(createRequest, requestSettings);
Sdk.jQ.execute(request).done(function(resp) {
var responses = resp.getResponses();
responses.forEach(function(responseItem) {
var id = responseItem.getResponse().getId();
console.log(id);
});
}).fail(function(error) {
console.log(error);
});
};
EDIT
After doing some more debugging, the response body is coming back with the correct guids, but when that response gets parsed the same guid gets added each time the parse function loops...and that's where I am stuck.
Response body:
<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">
<s:Body>
<ExecuteResponse xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\">
<ExecuteResult xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
<a:ResponseName>ExecuteMultiple</a:ResponseName>
<a:Results xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">
<a:KeyValuePairOfstringanyType>
<b:key>IsFaulted</b:key>
<b:value i:type=\"c:boolean\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">false</b:value>
</a:KeyValuePairOfstringanyType>
<a:KeyValuePairOfstringanyType>
<b:key>Responses</b:key>
<b:value i:type=\"c:OrganizationResponseCollection\" xmlns:c=\"http://schemas.microsoft.com/xrm/2012/Contracts\">
<c:ExecuteMultipleResponseItem>
<c:Fault i:nil=\"true\"/>
<c:RequestIndex>0</c:RequestIndex>
<c:Response>
<a:ResponseName>Create</a:ResponseName>
<a:Results>
<a:KeyValuePairOfstringanyType>
<b:key>id</b:key>
<b:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">df07d3fb-862c-e511-bdfe-00155d01050d</b:value>
</a:KeyValuePairOfstringanyType>
</a:Results>
</c:Response>
</c:ExecuteMultipleResponseItem>
<c:ExecuteMultipleResponseItem>
<c:Fault i:nil=\"true\"/>
<c:RequestIndex>1</c:RequestIndex>
<c:Response>
<a:ResponseName>Create</a:ResponseName>
<a:Results>
<a:KeyValuePairOfstringanyType>
<b:key>id</b:key>
<b:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">e107d3fb-862c-e511-bdfe-00155d01050d</b:value>
</a:KeyValuePairOfstringanyType>
</a:Results>
</c:Response>
</c:ExecuteMultipleResponseItem>
</b:value>
</a:KeyValuePairOfstringanyType>
</a:Results>
</ExecuteResult>
</ExecuteResponse>
</s:Body>
</s:Envelope>
The ExecuteMultipleResponse function in Microsoft's Sdk.ExecuteMultiple.js taken from here: https://code.msdn.microsoft.com/SdkSoapjs-9b51b99a/sourcecode?fileId=113716&pathId=823928626 The parseResponse function is where I see the same guid get set but I don't know enough about parsing xml to know where this might be going wrong.
this.ExecuteMultipleResponse = function (responseXml) {
///<summary>
/// Response to ExecuteMultipleRequest
///</summary>
if (!(this instanceof Sdk.ExecuteMultipleResponse)) {
return new Sdk.ExecuteMultipleResponse(responseXml);
}
Sdk.OrganizationResponse.call(this)
// Internal properties
var _isFaulted = null;
var _responses = null;
// Internal property setter functions
function _setIsFaulted(xml) {
var valueNode = Sdk.Xml.selectSingleNode(xml, "//a:KeyValuePairOfstringanyType[b:key='IsFaulted']/b:value");
if (!Sdk.Xml.isNodeNull(valueNode)) {
_isFaulted = (Sdk.Xml.getNodeText(valueNode) == "true") ? true : false;
}
}
function _setResponses(xml) {
var valueNode = Sdk.Xml.selectSingleNode(xml, "//a:KeyValuePairOfstringanyType[b:key='Responses']/b:value");
if (!Sdk.Xml.isNodeNull(valueNode)) {
_responses = parseResponses(valueNode);
}
}
function parseResponses(xml) {
//Using Sdk.Collection rather than create a new class for Microsoft.Xrm.Sdk.ExecuteMultipleResponseItemCollection
var rv = new Sdk.Collection(Sdk.ExecuteMultipleResponseItem);
for (var i = 0; i < xml.childNodes.length; i++) {
var emri = new Sdk.ExecuteMultipleResponseItem();
emri.setRequestIndex(parseInt(Sdk.Xml.selectSingleNodeText(xml.childNodes[i], "l:RequestIndex"), 10));
var faultNode = Sdk.Xml.selectSingleNode(xml.childNodes[i], "l:Fault");
if (!Sdk.Xml.isNodeNull(faultNode)) {
emri.setFault(new Sdk.ExecuteMultipleFault(faultNode));
}
else {
var responseName = Sdk.Xml.selectSingleNodeText(xml.childNodes[i], "l:Response/a:ResponseName") + "Response";
var responseXml = Sdk.Xml.selectSingleNode(xml.childNodes[i], "l:Response/a:Results");
emri.setResponse(new Sdk[responseName](responseXml));
}
rv.add(emri);
}
return rv;
}
I am the author of the Sdk.Soap.js library and I can repro what you see.
You should be able to fix this by editing the parseResponses function in the Sdk.ExecuteMultiple.js file. Replace the commented line below so it includes .cloneNode(true).
//var responseXml = Sdk.Xml.selectSingleNode(xml.childNodes[i], "l:Response/a:Results");
var responseXml = Sdk.Xml.selectSingleNode(xml.childNodes[i].cloneNode(true), "l:Response/a:Results");
The problem is that when the node is passed through to the constructor of the Sdk.CreateResponse, the entire xml document was passed through and when it was evaluated using: Sdk.Xml.selectSingleNode(xml, "//a:KeyValuePairOfstringanyType[b:key='id']/b:value"); this found the first instance of the whole document rather than just the specified childNode.
Using cloneNode seems to address the issue, but I haven't done any testing on browsers other than IE. Please let me know if you find issues with other browsers.
Related
Hi I'm trying to create a rest response using post method, I want to dynamically pass the variables instead of hard coding,But where i fail is,when I'm trying to to send an array as a parameter to the Rest web service using post method(example array ["CN=XXX_XX,OU=XXXXX,OU=1_XXXX XXXXity Groups,DC=XXXX,DC=local"]) and I know that there is a better way to do that Please find my code sample.This is the method that gives me a appropriate result.
First Method:(Works)
`
try {
var r = new sn_ws.RESTMessageV2('SailPoint_IdM', 'post');
var txt = "{\r\n\t\"workflowArgs\":\r\n\t{\r\n\t\"identityName\":\"SiamR\",\r\n\t\"appName\":\"Active Directory\",\r\n\t\"listEntitlements\":[\"CN=ER_CxxxK,OU=xxxxx,OU=1_xxxxxx Security xxx,DC=xxxx,DC=local\"],\r\n\t\"operation\":\"Add\",\r\n\t\"ticketNumber\":\"RITM1234567\"\r\n\t}\r\n}";
r.setRequestBody(txt);
var response = r.execute();
var ResponseBody = response.getBody();
var HTTPCode = response.getStatusCode();
gs.log(ResponseBody);
gs.log(HTTPCode);
} catch (ex) {
var message = ex.getMessage();
}
output:
Script: {"attributes":{"requestResult":{"status":"Success"}},"complete":false,"errors":null,"failure":false,"metaData":null,"requestID":"2c988d8c5bd47cf7015bebfb64cf01e6","retry":false,"retryWait":0,"status":null,"success":false,"warnings":null}
Script: 200
2n Method (Does not Work):
try {
var r = new sn_ws.RESTMessageV2('SailPoint_IdM', 'post');
r.setStringParameter('"listEntitlements"', '["CN=Exxx_xxxK,OU=xxxxion,OU=1_xxxxx Security xxxx,DC=xxx,DC=xxxx"]');
r.setStringParameter('"identityName"', '"SiarmR"');
r.setStringParameter('"appName"', '"Active Directory"');
r.setStringParameter('"ticketNumber"', '"RITM1234567"');
r.setStringParameter('operation', '"Add"');
//override authentication profile
//authentication type ='basic'/ 'oauth2'
//r.setAuthentication(authentication type, profile name);
var response = r.execute();
var responseBody = response.getBody();
var httpStatus = response.getStatusCode();
gs.log(responseBody );
}
catch(ex) {
var message = ex.getMessage();
}
output:
Script: {"attributes":{"requestResult":{"errors":["An unexpected error occurred: sailpoint.tools.GeneralException: The application script threw an exception: java.lang.NullPointerException: Null Pointer in Method Invocation BSF info: script at line: 0 column: columnNo"],"status":"FAIL","GroupStatus":null,"AppStatus":null}},"complete":false,"errors":["Status : failed\nAn unexpected error occurred: sailpoint.tools.GeneralException: The application script threw an exception: java.lang.NullPointerException: Null Pointer in Method Invocation BSF info: script at line: 0 column: columnNo\n"],"failure":false,"metaData":null,"requestID":null,"retry":false,"retryWait":0,"status":null,"success":false,"warnings":null}
Script: 200
Im facing issue with this parameter as im trying to pass this as aray paramenter '["CN=Exxx_xxxK,OU=xxxxion,OU=1_xxxxx Security xxxx,DC=xxx,DC=xxxx"]'
Please suggest a way to implement this and to pass all the variables dynamically if suggesting first method
Below is one of my function, to handle dynamic parameters in either appear in request endpoint (url), headers or body;
For eg: parameter p
var p = {abc: 'def'};
and outbuond rest settings:
rest url = https://xxxx.sss.com/api/showme?name=${abc}
rest headers name = custom-header; value = ${abc}
rest body = {name: "${abc}"}
so it will replace all ${abc} to 'def'
_.isNullOrEmpty - check is obj, string or array is null or empty;
_.loop - loop an obj or array, pass in function(nm/i, val) {}
_.isArray - to check if is array
_.str - convert anything to string
_.rpl - replace all string A to B
restParameters: function (restRequest, obj, endpoint) {
var _ = this;
if ((_.isNullOrEmpty(restRequest)) || (_.isNullOrEmpty(obj))) return;
if (_.isNullOrEmpty(endpoint)) endpoint = restRequest.getEndpoint();
var body = restRequest.getRequestBody();
var headers = restRequest.getRequestHeaders();
_.loop(obj, function(nm, val) {
if (_.isArray(val)) {
val = (_.isNullOrEmpty(val)) ? '[]' : JSON.stringify(val);
} else val = _.str(val);
//for my case my array pass in as string become: "[\"1\", \"2\"]"
//comment below if pass in as object
if (val.contains('"')) val = _.rpl(val, '"', '\\"');
restRequest.setStringParameterNoEscape(nm, val);
var sch = '${' + nm + '}';
endpoint = _.rpl(endpoint, sch, val);
body = _.rpl(body, sch, val);
_.loop(headers, function (hn, hv) {
headers[hn] = _.rpl(hv, sch, val);
});
}, true);
restRequest.setEndpoint(endpoint);
restRequest.setRequestBody(body);
_.loop(headers, function (hn, hv) { restRequest.setRequestHeader(hn, hv); });
}
All the variables are returning correct values but the the urlfetch response returns 403 or 401 (access denied).
First log output:
var payload = {
"apikey": API_KEY,
"filters": {
"sendtime_start": REPORT_START_DATE,
"sendtime_end": REPORT_END_DATE
}
};
Logger.log(payload );
Second log output:
var params = {
"method": "POST", //what MC specifies
"muteHttpExceptions": true,
"payload": payload,
"limit": 100
};
Logger.log(params);
Third log output:
var apiCall = function(endpoint) {
//issue with syntax here?
var apiResponse = UrlFetchApp.fetch(automationsList, params);
var json = JSON.parse(apiResponse);
Logger.log(apiResponse);
return json;
};
Automation API Call that is not working:
var automations = apiCall(automationsList);
var automationsData = automations.data;
for (var i = 0; i < automationsData.length; i++) {
// are these response parameters? are these specific values getting pulled from MC - these are the type of values i want?
var a = automationsData[i];
var aid = a.id; // identifies unique campaign *** does this have anything to do with the call function above - it used to be as cid b/c this was for campaigns before??
var emails_sent = a.emails_sent;
var recipients = a.recipients;
var report_summary = a.report_summary;
var settings = a.settings;
if (send_time) {
var r = apiCall(reports, cid); // why does this have cid? but the other one didn't??
var emails_sent = r.emails_sent;
var opens = r.opens;
var unique_opens = r.unique_opens;
var clicks = r.clicks;
var unique_clicks = r.unique_clicks;
var open_rate = (unique_opens / emails_sent).toFixed(4);
var click_rate = (unique_clicks / emails_sent).toFixed(4);
}
The for loop is not even gets executed because I get following error for automationsData:
TypeError: Cannot read property "data" from undefined. (line 82, file "Code")
The apiResponse there is somehow not working, any help is appreciated.
The problem is in how you set up your project in the Developers Console. Try to follow again the process here for you to verify if you already do it in the correct way.
You can also check the solution here in this SO question, he/she explained it here, why he/she get the same 401 and 403 error that you get.
As it turns out, I was using v3.0 for the Mailchimp api whereas I needed to use 2.0.
Well i have created a function to return the picture url. See code below:
function loadAttachment(itemid) {
web = context.get_web();
attachmentFolder = web.getFolderByServerRelativeUrl("Lists/LijstMedewerkers/Attachments/" + itemid);
attachmentFiles = attachmentFolder.get_files();
//Load attachments
context.load(attachmentFiles);
context.executeQueryAsync(onLoaddAttachmentSuccess, onLoadAttachmentFail);
alert(picture);
return picture;
}
function onLoadAttachmentFail(sender, args) {
alert('Failed to get lists items. Error:' + args.get_message());
}
function onLoaddAttachmentSuccess(sender, args) {
// Enumerate and list the Asset Attachments if they exist
var attachementEnumerator = attachmentFiles.getEnumerator();
while (attachementEnumerator.moveNext()) {
var attachment = attachementEnumerator.get_current();
picture = attachment.get_serverRelativeUrl();
}
}
Well it's not returning the value of the picture. When i do an alert i see the value but with the return it's not working. even if i put the picture in the itemid.
Any idea what i'm doing wrong ?
Since SP.ClientContext.executeQueryAsync method is async:
SP.ClientContext.executeQueryAsync(succeededCallback, failedCallback)
succeededCallback is used for declaring function that contains the returned results.
When working with asynchronous API such as JSOM the following patterns are commonly used:
Using nested callbacks
Using the promises pattern
The below example demonstrates how to retrieve attachment files using callback approach:
function loadAttachments(listTitle, itemId,success,error) {
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
var listItem = list.getItemById(itemId);
var files = listItem.get_attachmentFiles();
context.load(files);
context.executeQueryAsync(function(){
success(files);
},
error);
}
Usage
Get first file attachment url
loadAttachments('Projects',3,
function(attachmentFiles){
if(attachmentFiles.get_count() > 0) {
var attachmentFile = attachmentFiles.getItemAtIndex(0);
var fileUrl = attachmentFile.get_serverRelativeUrl();
//...
}
},
function(sender,args){
console.log(args.get_message());
});
I'm writing a jQuery plugin for work which pulls in RSS feed data using Google's Feed API. Using this API, I'm saving all of the relevant RSS feed data into an object, then manipulating it through methods. I have a function which is supposed to render the RSS feed onto the webpage. Unfortunately, when I try to display the individual RSS feed entries, I get an error. Here's my relevant code:
var RSSFeed = function(feedTitle, feedUrl, options) {
/*
* An object to encapsulate a Google Feed API request.
*/
// Variables
this.description = "";
this.entries = [];
this.feedUrl = feedUrl;
this.link = "";
this.title = feedTitle;
this.options = $.extend({
ssl : true,
limit : 4,
key : null,
feedTemplate : '<article class="rss-feed"><h2>{title}</h1><ul>{entries}</ul></article>',
entryTemplate : '<li><h3>{title}</h3><p>by: {author} # {publishedDate}</p><p>{contentSnippet}</p></li>',
outputMode : "json"
}, options || {});
this.sendFeedRequest = function() {
/*
* Makes the AJAX call to the provided requestUrl
*/
var self = this;
$.getJSON(this.encodeRequest(), function(data) {
// Save the data in a temporary object
var responseDataFeed = data.responseData.feed;
// Now load the data into the RSSFeed object
self.description = responseDataFeed.description;
self.link = responseDataFeed.link;
self.entries = responseDataFeed.entries;
});
};
this.display = function(jQuerySelector) {
/*
* Displays the RSSFeed onto the webpage
* Each RSSEntry will be displayed wrapped in the RSSFeed's template HTML
* The template markup can be specified in the options
*/
var self = this;
console.log(self);
console.log(self.entries);
};
};
$.rssObj = function(newTitle, newUrl, options) {
return new RSSFeed(newTitle, newUrl, options);
};
// Code to call the jquery plugin, would normally be found in an index.html file
rss = $.rssObj("Gizmodo", "http://feeds.gawker.com/Gizmodo/full");
rss.sendFeedRequest();
rss.display($('div#feed'));
Obviously, my display() function isn't complete yet, but it serves as a good example. The first console.log() will write all of the relevant data to the console, including the entries array. However, when I try to log the entries array by itself, it's returning an empty array. Any idea why that is?
I guess the problem is that display() is called without waiting for the AJAX request to complete. So the request is still running while you already try to access entries - hence the empty array.
In order to solve this you could move the call to display() into the callback of $.getJSON(). You just have to add the required selector as a parameter:
this.sendFeedRequest = function(selector) {
var self = this;
$.getJSON(this.encodeRequest(), function(data) {
var responseDataFeed = data.responseData.feed;
...
self.entries = responseDataFeed.entries;
self.display(selector);
});
};
EDIT:
If you don't want to move display() into the callback, you could try something like this (untested!):
var RSSFeed = function(feedTitle, feedUrl, options) {
...
this.loading = false;
this.selector = null;
this.sendFeedRequest = function() {
var self = this;
self.loading = true;
$.getJSON(this.encodeRequest(), function(data) {
...
self.loading = false;
if (self.selector != null) {
self.display(self.selector);
}
});
};
this.display = function(jQuerySelector) {
var self = this;
if (self.loading) {
self.selector = jQuerySelector;
}
else {
...
}
};
};
I'm having an issue where I get an error that says...
"Uncaught TypeError: Object f771b328ab06 has no method 'addLocation'"
I'm really not sure what's causing this. The 'f771b328ab06' is a user ID in the error. I can add a new user and prevent users from being duplicated, but when I try to add their location to the list, I get this error.
Does anybody see what's going wrong? The error occurs in the else statement of the initialize function as well (if the user ID exists, just append the location and do not create a new user). I have some notes in the code, and I'm pretty sure that this is partly due to how I have modified an example provided by another user.
function User(id) {
this.id = id;
this.locations = [];
this.getId = function() {
return this.id;
};
this.addLocation = function(latitude, longitude) {
this.locations[this.locations.length] = new google.maps.LatLng(latitude, longitude);
alert("User ID:" );
};
this.lastLocation = function() {
return this.locations[this.locations.length - 1];
};
this.removeLastLocation = function() {
return this.locations.pop();
};
}
function Users() {
this.users = {};
//this.generateId = function() { //I have omitted this section since I send
//return Math.random(); //an ID from the Android app. This is part of
//}; //the problem.
this.createUser = function(id) {
this.users[id] = new User(id);
return this.users[id];
};
this.getUser = function(id) {
return this.users[id];
};
this.removeUser = function(id) {
var user = this.getUser(id);
delete this.users[id];
return user;
};
}
var users = new Users();
function initialize() {
alert("Start");
$.ajax({
url: 'api.php',
dataType: 'json',
success: function(data){
var user_id = data[0];
var latitude = data[1];
var longitude = data[2];
if (typeof users.users[user_id] === 'undefined') {
users.createUser(user_id);
users.users[user_id] = "1";
user_id.addLocation(latitude, longitude); // this is where the error occurs
}
else {
user_id.addLocation(latitude, longitude); //here too
alert(latitude);
}
}
})
}
setInterval(initialize, 1000);
Since I get the ID from the phone and do not need to generate it here (only receive it), I commented out the part that creates the random ID. In doing this, I had to add a parameter to the createUser method within Users() so that I can pass the ID as an argument from Initialize(). See the changes to createUser below:
Before, with the generated ID (the part where the number is generated is in the above code block with comments):
this.createUser = function() {
var id = this.generateId();
this.users[id] = new User(id);
return this.users[id];
};
After, with the ID passed as an argument:
this.createUser = function(id) {
this.users[id] = new User(id);
return this.users[id];
};
If anyone has any suggestions I would really appreciate it. Thanks!
Here you're getting user_id by :
var user_id = data[0];
So it's a part of the json answer : maybe a string or another dictionnary, this can't be a user object. You should try to update your code in your success function inside the "if" block by :
user = users.createUser(user_id);
//The following line is a non sense for me you put an int inside
//an internal structure of your class that should contain object
//users.users[user_id] = "1";
user.addLocation(latitude, longitude);