Firebase https cloud function to send notifications always ends in timeout - firebase

Function execution took 60002 ms, finished with status: 'timeout'
I have cloud function that collects users product & price data, hits an external API to get the latest prices and sends a notification if the price has changed. I've read several similar questions like this but I am in fact sending a response back to the client. I've tried increasing the timeout to 120 seconds, but it still times out. So, I think there might be an issue with how I'm returning promises? Any suggestions?
Here is my code:
exports.pushTestWithRP = functions.https.onRequest((req, res) => {
var uidsAndTokens = [];
ref.child('tokens').once('value').then(snap => {
snap.forEach(childSnap => {
uidsAndTokens.push({
uid: childSnap.key,
deviceToken: childSnap.val()
});
});
return uidsAndTokens;
}).then((uidsAndTokens) => {
var uidsAndCruises = [];
ref.child('watchlist-items/users').once('value').then(snap => {
snap.forEach(childSnap => {
var uid = childSnap.key;
childSnap.forEach(childChildSnap => {
var product = childChildSnap.key;
var productWatchInfo = childChildSnap.val();
uidsAndProducts.push({
uid: uid,
product: product,
watchInfo: productWatchInfo
});
}); // end childChildSnap
}); // end childSnap
return uidsAndProducts;
}).then((uidsAndProducts) => { // end snap watchlist-items/users
var uidsOnly = [];
for (var i=0; i<uidsAndTokens.length; i++) {
uidsOnly.push(uidsAndTokens[i].uid);
}
// user has a FCM token
var uidsAndProductsWithTokens = [];
for (var i=0; i<uidsAndProducts.length; i++) {
//check if exists in tokens array
var currUid = uidsAndProducts[i].uid;
if (uidsOnly.includes(currUid)) {
//console.log('this uid has a token: ', currUid);
uidsAndProductsWithTokens.push(uidsAndProducts[i]);
} else {
//this uid does NOT have a token
}
}
function getTokenForUid(uid) {
for (var i in uidsAndTokens) {
if (uidsAndTokens[i].uid == uid) {
var deviceToken = uidsAndTokens[i].deviceToken;
break;
}
}
return deviceToken;
}
var allPromises = [];
// call API only for uids with tokens
for (var i=0; i<uidsAndProductsWithTokens.length; i++) {
const product = uidsAndProductsWithTokens[i].product;
const uid = uidsAndProductsWithTokens[i].uid;
const deviceToken = getTokenForUid(uid);
const pDates = uidsAndProductsWithTokens[i].watchInfo.pDates;
const pName = uidsAndProductsWithTokens[i].watchInfo.pName;
getCurrentPricesFromAPI(product).then((response) => {
if (typeof response.response != 'undefined') {
const productId = response.response.product.product_id;
const allPrices = response.response.prices;
const promises = [];
// parse thru prices and send notifications
for (var date in pDates) {
// get all current prices and sort by price to get cheapest
var cheapest = [];
for (var i = 0; i < allPrices.length; i++) {
if (allPrices[i].data[productId][date] && allPrices[i].data[productId][date].hasOwnProperty('Inside')) {
const iPrice = allPrices[i].data[productId][date].Inside;
cheapest.push(iPrice);
}
}
if (cheapest[0] > 0) {
cheapest = cheapest.sort(function (a, b) { return a - b; });
if (sDates[date].hasOwnProperty('Inside')) {
const priceDiff = cheapest[0] - sDates[date].Inside.price;
if (priceDiff < -10) {
const payload = {
notification: {
title: pName + ' Price DROP Alert!',
body: 'prices for the ' + date + ' are $' + cheapest[0] + ' (DOWN $' + Math.abs(priceDiff) + ')',
sound: 'default'
}
};
promises.push(admin.messaging().sendToDevice(deviceToken, payload));
}
else if (priceDiff > 10) {
const payload = {
notification: {
title: pName + ' Price Hike Alert',
body: 'prices for the ' + date + ' are $' + cheapest[0] + ' (UP $' + priceDiff + ')',
sound: 'default'
}
};
promises.push(admin.messaging().sendToDevice(deviceToken, payload));
}
}
}
}
allPromises = allPromises.concat(promises);
}
}) // end handle API response
} // end for loop
return allPromises;
}).then((allPromises) => {
return Promise.all(allPromises);
res.send('got tokens and ids');
}).catch(error => {
res.send('there was an error');
});
}); // end uidsAndTokens
}); // end function
I can't figure this out and would appreciate any help!

Related

Firestore transaction update multiple document

I want to update documents in one collection.
my_collection
document_1
field_1
field_2
document_2
field_1
field_2
My code:
exports.aggregateUsers =
functions.firestore.document('users/{userId}').onWrite(function(change,
context) {
const document = change.after.exists ? change.after.data() : null;
const oldDocument = change.before.data() || null;
return firestore.runTransaction(function(transaction) {
var oldInstanceRef;
var instanceRef;
var oldInstanceDoc;
var instanceDoc;
if (document != null) {
instanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(document.instance);
instanceDoc = transaction.get(instanceRef);
var newNumberOfUsers = (instanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newNumberOfUsers });
}
if (oldDocument != null) {
oldInstanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(oldDocument.instance);
oldInstanceDoc = transaction.get(oldInstanceRef);
var newPrevNumberOfUsers = (oldInstanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newPrevNumberOfUsers });
}
}).catch(function(error) {
console.log("invalid-argument", error.code, error.message);
});
});
Errors:
instanceDoc.data is not a function
I am using transcation in cloud function to aggregate number of users, not using distributed counter, because of low traffic app. My question is how to update field's value in each document? Thank you
I guess you forgot the async/await.
Try this:
async before transaction parameter
await before transaction.get(<some_ref_here>)
return firestore.runTransaction(async transaction => {
var oldInstanceRef;
var instanceRef;
var oldInstanceDoc;
var instanceDoc;
if (document != null) {
instanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(document.instance);
instanceDoc = await transaction.get(instanceRef);
var newNumberOfUsers = (instanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newNumberOfUsers });
}
if (oldDocument != null) {
oldInstanceRef = firestore.collection("counters").doc("instance_counter").collection("instances").doc(oldDocument.instance);
oldInstanceDoc = await transaction.get(oldInstanceRef);
var newPrevNumberOfUsers = (oldInstanceDoc.data().number_of_users || 0) + 1;
transaction.set(instanceRef, { number_of_users: newPrevNumberOfUsers });
}
}).catch(function(error) {
console.log("invalid-argument", error.code, error.message);
});

Cordova SQLite class - cannot return value from database

So, I'm building an app with Cordova and SQLite and below is the object Storage I created to handle the database properties and methods.
function Storage(connection, platform)
{
if (platform == 'mobile') {
this.db = window.sqlitePlugin.openDatabase({name: connection.name, location: connection.location});
} else if (platform == 'web') {
this.db = window.openDatabase(connection.name, connection.version, connection.mode, -1);
}
this.read = function(sql, vals) {
var rows = null;
this.db.transaction(function(tx) {
tx.executeSql(sql, vals, function(tx, res) {
rows = res.rows;
});
}, function(error) {
console.log('Transaction error: '+error.message);
});
return rows;
};
};
var connection = {
name: 'database.db',
version: '1.0',
mode: 'Development'
};
var db = new Storage(connection, 'web');
var sql = '';
sql += 'SELECT *';
sql += ' FROM countdown';
sql += ' WHERE countdown_status != ?;';
var rows = db.read(sql, [1]);
console.log(rows);
This code logs 'null' and I don't know why since I have data into my database. And when I try to log from within the tx.executeSql method, it logs the data from database.
Any ideas on why I cannot get this data out of that function?
Thanks a lot.
the db read is asynchronous - it needs to return the data to a callback function like so:
getHighScore: function (type,callback) {
var query = "SELECT Value FROM HighScore where Type = '" + type + "';";
playedDb.transaction(function (tx) {
tx.executeSql(query, [], function (tx, res) {
var out;
if (typeof res.rows.item(0) === "undefined") {
if (typeof callback === "function") {
callback(-1);
return;
};
} else {
out = res.rows.item(0).Value
//task 301 only allow alphanumeric and decimal point (for version)
out = String(out).replace(/[^0-9a-z\.]/gi, '');
if (typeof callback === "function") {
callback(out);
return;
};
}
}, function (e) {
console.log("getHighScore FAILED: " + e.message);
if (typeof callback === "function") {
callback(-2);
return;
};
});

Firebase infinite loop insert my item on child_added/set

I got an infinite loop inserting an item to my Firebase, so when I click on my post form, it inserts my item until I kill the process. Can you help me how to solve it?
PS : I'm using VueJS
var usersRef = new Firebase('https://xxxxxxxxxxxxxxxxxx.firebaseio.com/userslist/');
var vm = new Vue({
el: '#list1',
data: function () {
return{
// Initialisation du tableau de users
users: [],
sortKey: 'id',
reverse: 1,
nextKey: null
};
},
ready: function () {
// this works
//this.sortKey = 'name';
},
methods: {
updateUsers: function () {
},
removeUser: function (item) {
usersRef.child(item.id).remove();
},
addItem: function (e) {
e.preventDefault();
// get form data as Json
var jsonData = ConvertFormToJSON('form_add');
//console.log(jsonData);//test ok
//get the last item id and save it to next key
usersRef.limitToLast(1).on('child_added', function (snapshot) {
var lastKey = parseInt(snapshot.key());
this.nextKey = lastKey + 1;
console.log('nextKey ' + nextKey);//test ok
//
// save data to firebase
usersRef.child(this.nextKey).set(jsonData, function (snap) {
//console.log('add success');//test
//Notification par Jquery
var itemAdded = snap.val();
$.notify(itemAdded.firstname + " " + itemAdded.name + " à été ajouté", "success", {position: "top right"});
this.pendingKey = 0;
});
});
},
// Tri des colonnes
sortBy: function (_sortKey) {
this.reverse = (this.reverse == -1) ? 1 : -1;
this.sortKey = _sortKey;
console.log("SortKey " + this.sortKey);
}
}
});
usersRef.on('child_added', function (snapshot) {
var item = snapshot.val();
item.id = snapshot.key();
console.log('id ' + item.id);
vm.users.push(item);
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
thanks for your help
OK I've found a solution :
//get the last item id and save it to next key
var _nextKey = this.nextKey;
usersRef.limitToLast(1).on('child_added', function (snapshot) {
var lastKey = parseInt(snapshot.key());
_nextKey = lastKey + 1;
});
this.nextKey = _nextKey;
console.log('nextKey ' + this.nextKey);//test ok
// save data to firebase
usersRef.child(this.nextKey).set(jsonData, function (snap) {
//console.log('add success');//test
//Notification par Jquery
var itemAdded = snap.val();
$.notify(itemAdded.firstname + " " + itemAdded.name + " à été ajouté", "success", {position: "top right"});
});
May it help someone!

sequence of select query not right

I tried to query my database for data. However, when I query for my data, I only managed to get the data after my last alert, which is the alert("Total...") For instance, I run the code below, the alert for "result row:" only appear after the last alert appear. May I know why?
function (result) {
window.location.href = "#/app/CustHomePage";
var totalBalance = 0;
var tableRef = document.getElementById("myList").getElementsByTagName("tbody")[0];
for (var i = 0; i < tableRef.rows.length; i++) {
alert(tableRef.rows[i].cells[3].innerHTML.substr(1));
var addBalance = parseInt(tableRef.rows[i].cells[3].innerHTML.substr(1));
totalBalance = totalBalance + addBalance;
}
alert(totalBalance);
var myText = result.text;
var myTextTwo = myText.replace(/['"]+/g, '');
alert(myTextTwo);
var custBal;
var myDB = window.sqlitePlugin.openDatabase({ name: "mySQLite.db", location: 'default' });
alert("Hello");
myDB.transaction(function(transaction) {
alert("Hello");
transaction.executeSql("SELECT customerBalance FROM customer where nric = '" + myTextTwo + "'", [], function (tx, results) {
var len = results.rows.length;
for (var i = 0; i < len; i++) {
custBal = results.rows.item(i).customerBalance;
alert("result row" + results.rows.item(i).customerBalance);
alert("CustomerBal" + custBal);
}
},
function(tx, error)
{
}
);
});
alert("Total after entitled discount: ");
alert("Transaction successful, Next Customer Please");
}
Move anything you want to happen after the DB transaction has returned or failed in either the success or error handlers, I just moved them out of the arguments and gave them names to keep track of them. and then added your comment to the end of the successCallback handler. Anything you want to happen after a transaction was not successful use the error callback.
function successCallback(tx, results) {
var len = results.rows.length;
for (var i = 0; i < len; i++) {
custBal = results.rows.item(i).customerBalance;
alert("result row" + results.rows.item(i).customerBalance);
alert("CustomerBal" + custBal);
}
alert("Total after entitled discount: ");
alert("Transaction successful, Next Customer Please");
}
function errorCallback(tx, error) {
}
myDB.transaction(function (transaction) {
alert("Hello");
transaction.executeSql("SELECT customerBalance FROM customer where nric = '" + myTextTwo + "'", [], successCallback, errorCallback);
});

On Approval Of Quotation, Run Report, Generate PDF and Send an Email With PDF as attachment

I am using CRM ONLINE 2013.
How to automate below process?
On Approval Of Quotation, Run Report.
Generate PDF.
Send an Email With PDF as attachment.
As I have gone through many forums for this topic, but creating a plugin code for generating Report PDF is not possible in CRM ONLINE.
What is the alternate way to do this..?
I have tried below code and it is working fine for me.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
<script type="text/javascript">
if (typeof (SDK) == "undefined")
{ SDK = { __namespace: true }; }
SDK.JScriptRESTDataOperations = {
_context: function () {
if (typeof GetGlobalContext != "undefined")
{ return GetGlobalContext(); }
else {
if (typeof Xrm != "undefined") {
return Xrm.Page.context;
}
else { return new Error("Context is not available."); }
}
},
_getServerUrl: function () {
var serverUrl = this._context().getServerUrl()
if (serverUrl.match(/\/$/)) {
serverUrl = serverUrl.substring(0, serverUrl.length - 1);
}
return serverUrl;
},
_ODataPath: function () {
return this._getServerUrl() + "/XRMServices/2011/OrganizationData.svc/";
},
_errorHandler: function (req) {
return new Error("Error : " +
req.status + ": " +
req.statusText + ": " +
JSON.parse(req.responseText).error.message.value);
},
_dateReviver: function (key, value) {
var a;
if (typeof value === 'string') {
a = /Date\(([-+]?\d+)\)/.exec(value);
if (a) {
return new Date(parseInt(value.replace("/Date(", "").replace(")/", ""), 10));
}
}
return value;
},
Create: function (object, type, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", this._ODataPath() + type + "Set", true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
if (this.status == 201) {
successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
}
else {
errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
},
Retrieve: function (id, type, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("GET", this._ODataPath() + type + "Set(guid'" + id + "')", true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
if (this.status == 200) {
successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
}
else {
errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
}
}
};
req.send();
},
Update: function (id, object, type, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("X-HTTP-Method", "MERGE");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
},
Delete: function (id, type, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("X-HTTP-Method", "DELETE");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
}
}
};
req.send();
},
RetrieveMultiple: function (type, filter, successCallback, errorCallback) {
if (filter != null) {
filter = "?" + filter;
}
else { filter = ""; }
var req = new XMLHttpRequest();
req.open("GET", this._ODataPath() + type + "Set" + filter, true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
if (this.status == 200) {
successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d.results);
}
else {
errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
}
}
};
req.send();
},
__namespace: true
};
</script>
<script type="text/javascript">
//Create Email and link it with Order as Regarding field
var Xrm;
var email = new Object();
var ownerID = "";
var CustomerId = "";
if (window.opener) { Xrm = window.opener.Xrm; }
else if (window.parent) { Xrm = window.parent.Xrm; }
//Get ownerid who send email of quotation to customer
function GetOwnerID() {
var owner = Xrm.Page.getAttribute("ownerid").getValue();
ownerID = owner[0].id;
var ownerName = owner[0].name;
var entityType = owner[0].entityType;
GetToEmailGUID();
}
//Get customerid who receive email of quotation from owner
function GetToEmailGUID() {
var Customer = Xrm.Page.getAttribute('customerid').getValue();
CustomerId = Customer[0].id;
var CustomerName = Customer[0].name;
var entityType = Customer[0].entityType;
//if CustomerId is type of "Account" then get Primary Contact id of that account
if (entityType == "account") {
var contact = Xrm.Page.getAttribute("customerid").getValue();
if (contact === null) return;
var serverUrl = Xrm.Page.context.getClientUrl();
var oDataSelect = serverUrl + "/XRMServices/2011/OrganizationData.svc/AccountSet(guid'" + contact[0].id + "')?$select=PrimaryContactId";
var req = new XMLHttpRequest();
req.open("GET", oDataSelect, false);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json;charset=utf-8");
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 200) {
var retrieved = JSON.parse(req.responseText).d;
CustomerId = retrieved.PrimaryContactId.Id;
}
else {
alert(this.statusText);
}
}
};
req.send();
}
}
function CreateEmail() {
GetOwnerID();
email.Subject = "Email with Report Attachment";
//Set The current order as the Regarding object
email.RegardingObjectId = {
Id: Xrm.Page.data.entity.getId(), //Get the current entity Id , here OrderId
LogicalName: Xrm.Page.data.entity.getEntityName()//Get the current entity name, here it will be “salesOrder”
};
//Create Email Activity
SDK.JScriptRESTDataOperations.Create(email, "Email", EmailCallBack, function (error) { alert(error.message); });
}
// Email Call Back function
function EmailCallBack(result) {
email = result; // Set the email to result to use it later in email attachment for retrieving activity Id
var activityPartyFrom = new Object();
// Set the From party of the ActivityParty to relate an entity with Email From field
activityPartyFrom.PartyId = {
Id: CustomerId, //"79EBDD26-FDBE-E311-8986-D89D6765B238", // id of entity you want to associate this activity with.
LogicalName: "contact"
};
// Set the "activity" of the ActivityParty
activityPartyFrom.ActivityId = {
Id: result.ActivityId,
LogicalName: "email"
};
// Now set the participation type that describes the role of the party on the activity).
activityPartyFrom.ParticipationTypeMask = { Value: 2 }; // 2 means ToRecipients
// Create the from ActivityParty for the email
SDK.JScriptRESTDataOperations.Create(activityPartyFrom, "ActivityParty", ActivityPartyFromCallBack, function (error) { alert(error.message); });
var activityPartyTo = new Object();
// Set the From party of the ActivityParty to relate an entity with Email From field
activityPartyTo.PartyId = {
Id: ownerID, //"79EBDD26-FDBE-E311-8986-D89D6765B238", // id of entity you want to associate this activity with.
LogicalName: "systemuser"
};
// Set the "activity" of the ActivityParty
activityPartyTo.ActivityId = {
Id: result.ActivityId,
LogicalName: "email"
};
// Now set the participation type that describes the role of the party on the activity).
activityPartyTo.ParticipationTypeMask = { Value: 1 }; // 1 means Sender
// Create the from ActivityParty
SDK.JScriptRESTDataOperations.Create(activityPartyTo, "ActivityParty", ActivityPartyToCallBack, function (error) { alert(error.message); });
}
//ActivityParty From Callback
function ActivityPartyFromCallBack(result) {
}
//ActivityParty To Callback
function ActivityPartyToCallBack(result) {
GetReportId('Quotation');
}
//Create attachment for the created email
function CreateEmailAttachment() {
//get reporting session and use the params to convert a report in PDF
var params = getReportingSession();
//Email attachment parameters
var activitymimeattachment = Object();
activitymimeattachment.ObjectId = Object();
activitymimeattachment.ObjectId.LogicalName = "email";
activitymimeattachment.ObjectId.Id = email.ActivityId;
activitymimeattachment.ObjectTypeCode = "email",
activitymimeattachment.Subject = "File Attachment";
activitymimeattachment.Body = encodePdf(params);
activitymimeattachment.FileName = "Report.pdf";
activitymimeattachment.MimeType = "application/pdf";
//Attachment call
SDK.JScriptRESTDataOperations.Create(activitymimeattachment, "ActivityMimeAttachment", ActivityMimeAttachmentCallBack, function (error) { alert(error.message); });
}
//ActivityMimeAttachment CallBack function
function ActivityMimeAttachmentCallBack(result) {
var features = "location=no,menubar=no,status=no,toolbar=no,resizable=yes";
var width = "800px";
var height = "600px";
window.open(Xrm.Page.context.getServerUrl() + "main.aspx?etc=" + 4202 + "&pagetype=entityrecord&id=" + email.ActivityId, "_blank", features);
// To open window which works in outlook and IE both
//openStdWin(Xrm.Page.context.getServerUrl() + "main.aspx?etc=" + 4202 + "&pagetype=entityrecord&id=" + email.ActivityId, "_blank", width, height, features);
}
//This method will get the reportId based on a report name that will be used in getReportingSession() function
function GetReportId(reportName) {
var oDataSetName = "ReportSet";
var columns = "ReportId";
var filter = "Name eq '" + reportName + "'";
retrieveMultiple(oDataSetName, columns, filter, onSuccess);
}
function retrieveMultiple(odataSetName, select, filter, successCallback) {
var serverUrl = Xrm.Page.context.getServerUrl();
var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
var odataUri = serverUrl + ODATA_ENDPOINT + "/" + odataSetName + "?";
if (select) {
odataUri += "$select=" + select + "&";
}
if (filter) {
odataUri += "$filter=" + filter;
}
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: odataUri,
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function (data) {
if (successCallback) {
if (data && data.d && data.d.results) {
successCallback(data.d.results);
}
else if (data && data.d) {
successCallback(data.d);
}
else {
successCallback(data);
}
}
},
error: function (XmlHttpRequest, errorThrown) {
if (XmlHttpRequest && XmlHttpRequest.responseText) {
alert("Error while retrieval ; Error – " + XmlHttpRequest.responseText);
}
}
});
}
function onSuccess(data) {
reportId = data[0].ReportId.replace('{', ").replace('}', ");
CreateEmailAttachment(); // Create Email Attachment
}
//Gets the report contents
function getReportingSession() {
var pth = Xrm.Page.context.getServerUrl() + "/CRMReports/rsviewer/reportviewer.aspx";
var retrieveEntityReq = new XMLHttpRequest();
var Id = Xrm.Page.data.entity.getId();
var quotationGUID = Id.replace('{', ""); //set this to selected quotation GUID
quotationGUID = quotationGUID.replace('}', "");
var reportName = "Quotation"; //set this to the report you are trying to download
var reportID = "7C39D18F-1DC6-E311-8986-D89D6765B238"; //set this to the guid of the report you are trying to download
var rptPathString = ""; //set this to the CRMF_Filtered parameter
var strParameterXML = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'><entity name='quote'><all-attributes /><filter type='and'><condition attribute='quoteid' operator='eq' uitype='quote' value='" + quotationGUID + "' /> </filter></entity></fetch>";
retrieveEntityReq.open("POST", pth, false);
retrieveEntityReq.setRequestHeader("Accept", "*/*");
retrieveEntityReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
rptPathString = "id=%7B" + reportID + "%7D&uniquename=" + Xrm.Page.context.getOrgUniqueName() + "&iscustomreport=true&reportnameonsrs=&reportName=" +
reportName + "&isScheduledReport=false&p:CRMAF_Filteredquote=" + strParameterXML;
//remove the part starting from &p:salesorderid if your report has no parameters
retrieveEntityReq.send(rptPathString);
var x = retrieveEntityReq.responseText.indexOf("ReportSession=");
var ret = new Array();
ret[0] = retrieveEntityReq.responseText.substr(x + 14, retrieveEntityReq.responseText.indexOf("&", x) - x - 14); //the session id
x = retrieveEntityReq.responseText.indexOf("ControlID=");
ret[1] = retrieveEntityReq.responseText.substr(x + 10, retrieveEntityReq.responseText.indexOf("&", x) - x - 10); //the control id
return ret;
}
var bdy = new Array();
var bdyLen = 0;
function concat2Bdy(x) {
bdy[bdyLen] = x;
bdyLen++;
}
function encodePdf(params) {
bdy = new Array();
bdyLen = 0;
var retrieveEntityReq = new XMLHttpRequest();
var pth = Xrm.Page.context.getServerUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] +
"&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] +
"&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";
retrieveEntityReq.open("GET", pth, false);
retrieveEntityReq.setRequestHeader("Accept", "*/*");
retrieveEntityReq.send();
BinaryToArray(retrieveEntityReq.responseBody);
return encode64(bdy);
}
var StringMaker = function () {
this.parts = [];
this.length = 0;
this.append = function (s) {
this.parts.push(s);
this.length += s.length;
}
this.prepend = function (s) {
this.parts.unshift(s);
this.length += s.length;
}
this.toString = function () {
return this.parts.join('');
}
}
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function encode64(input) {
var output = new StringMaker();
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
while (i < input.length) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output.append(keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4));
}
return output.toString();
}
</script>
<script type="text/vbscript">
Function BinaryToArray(Binary)
Dim i
ReDim byteArray(LenB(Binary))
For i = 1 To LenB(Binary)
byteArray(i-1) = AscB(MidB(Binary, i, 1))
concat2Bdy(AscB(MidB(Binary, i, 1)))
Next
BinaryToArray = byteArray
End Function
</script>
</head>
<body>
<input type="button" onclick="CreateEmail();" value="Attach Report" />
</body>
</html>

Resources