Thursday, November 28, 2019

Fetching Azure Blob File Attachments and listing in Dynamic 365 CRM


This blogpost is in a continuation of my previous blogpost where I had explained how to move attachment linked under Notes Section of Dynamic CRM to Azure Blob storage.


For a quick Demo, please refer to below video.




WebResource Output
Files are in hyperlink format and when clicked made Azure called to get the content of the file.

Custom Attachment Entity storing Attachment details when migrated to Azure Blob


WebResource Code content to display Notes details and generate a download link from Azure Blob using SAS_TOKEN authentication mechanism.

HTML Content

<html>
<body onload="resourceOnload()">
    <div id="div_attachmentContent"></div>
    <table id="tbl_listAttachments" style="border:none">
    </table>

</body>
</html>

JavaScript Content

<script>
var eks_Xrm = null;
var clientCrmUrl = null;

function initializeXrm() {
    if (typeof Xrm != 'undefined')
        eks_Xrm = Xrm;
    else if (typeof parent.Xrm != 'undefined')
        eks_Xrm = parent.Xrm;

    clientrmUrl = eks_Xrm.Page.context.getClientUrl();
}

function resourceOnload() {
    debugger;
    initializeXrm();
    var entityId = eks_Xrm.Page.data.entity.getId().replace('{', '').replace('}', '');
    var entitySetName = eks_Xrm.Page.data.entity.getEntitySetName();
    getAttachmentDetails(entitySetName, entityId);
}

function getAttachmentDetails(regardingEntityType, regardingEntityId) {
    var entity = "new_customattachments?";
    var selectClause = "$select=new_blobuniqueid,new_customattachmentid,new_filesize,new_filetype,new_fileurl,new_name,new_notedescrption,new_notetitle,new_regardingentityid,new_regardingentitytype";
    var filterClause = "&$filter=new_regardingentitytype eq '" + regardingEntityType + "' and  new_regardingentityid eq '" + regardingEntityId + "'";
    var orderbyClause = "&$orderby=createdon asc";

    var req = new XMLHttpRequest();
    req.open("GET", clientrmUrl + "/api/data/v9.1/" + entity + selectClause + filterClause + orderbyClause, false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var results = JSON.parse(this.response);
                bindDataToGrid(results);
            } else {
                Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
}

function bindDataToGrid(results) {
    debugger;
    var divOfAttachments = document.getElementById("div_attachmentContent");
    var tableOfAttachments = document.getElementById("tbl_listAttachments");

    if (results.value.length == 0) {
        divOfAttachments.innerHTML = "No Record Found";
        return;
    }

    for (var i = 0; i < results.value.length; i++) {
        var new_blobuniqueid = results.value[i]["new_blobuniqueid"];
        var new_customattachmentid = results.value[i]["new_customattachmentid"];
        var new_filesize = results.value[i]["new_filesize"];
        var new_filetype = results.value[i]["new_filetype"];
        var new_fileurl = results.value[i]["new_fileurl"];
        var new_name = results.value[i]["new_name"];
        var new_notedescrption = results.value[i]["new_notedescrption"];
        var new_notetitle = results.value[i]["new_notetitle"];
        var new_regardingentityid = results.value[i]["new_regardingentityid"];
        var new_regardingentitytype = results.value[i]["new_regardingentitytype"];

        var row1 = tableOfAttachments.insertRow(0);
        var row2 = tableOfAttachments.insertRow(1);
        var row3 = tableOfAttachments.insertRow(2);

        var title_td = row1.insertCell(0);
        title_td.colSpan = "2";
        title_td.style = "font:bold 12px Segoe UI; border:none";

        var description_td = row2.insertCell(0);
        description_td.colSpan = "2";
        description_td.style = "font:normal 11px Segoe UI; border:none";

        var file_td = row3.insertCell(0);
        file_td.style = "font:normal 11px Segoe UI; border-bottom:0.5px solid grey";

        title_td.innerHTML = new_notetitle;
        description_td.innerHTML = new_notedescrption;
        file_td.innerHTML = getUrlOfAttachmentFileFromBlob(new_fileurl,new_name);
    }
    divOfAttachments.appendChild(tableOfAttachments);
}

function getUrlOfAttachmentFileFromBlob(new_fileurl,new_fileName) {
    var fileHyperlink = '';
    var blobUri = 'https://' + 'Storage_Account_Name' + '.blob.core.windows.net';
    var containerName = 'vjtest';
    var sas_token = 'SAS_TOKEN';
    var blobService = AzureStorage.Blob.createBlobServiceWithSas(blobUri, sas_token).withFilter(new AzureStorage.Blob.ExponentialRetryPolicyFilter());

    var downloadLink = blobService.getUrl('vjtest', new_fileurl.replace('/'+containerName+'/',''), sas_token);

    if (downloadLink != null)
        fileHyperlink = ' + downloadLink + '" >' + new_fileName + '
'
    return fileHyperlink;
}
</script>

Note: You need to change highlighted parameter accordingly to get connected to Azure Blob. You may also think of storing them in custom configuration entity for security reasons.

Azure Blob Library Reference

We need to make Azure blob library as a CRM Resource and use it in a reference in our web-resource.

<script src="https://Vipinsandbox.crm4.dynamics.com//WebResources/new_/script/azure-storage.blob.js"></script>


Thanks
Vipin Jaiswal
vipinjaiswal12@gmail.com

4 comments:

Unknown said...

Nice Article 👍

Anonymous said...

Thanks for the information. Really help me a lot.

Anonymous said...

Hi,

In your above example you have used SAS_Token. Can you please share step by step how to create on Azure portal and pass it here.


Unknown said...

While clicking download link, I am getting "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature" error. What is the fix for this?

Thank you...