Azure Data Factory – Storage Integration

Using Azure Blob API

https://docs.microsoft.com/en-us/rest/api/storageservices/put-blob

We can configure credentials and values in the script parameters


</**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/search','N/file','N/https','N/encode','N/runtime'],
    /**
 * @param{search} search
 */
    (search,file,https,encode,runtime) => {

        /**
         * Defines the Scheduled script trigger point.
         * @param {Object} scriptContext
         * @param {string} scriptContext.type - Script execution context. Use values from the scriptContext.InvocationType enum.
         * @since 2015.2
         */
        const execute = (scriptContext) => {
            try{

              var currentScript = runtime.getCurrentScript();

              let azureAccount =  currentScript.getParameter("custscript_azure_account");
              let fileName=currentScript.getParameter("custscript_azure_file_name");
              let sharedkey = currentScript.getParameter("custscript_azure_shared_key");
              let storage = currentScript.getParameter("custscript_azure_storage");
              let savedSearchId = currentScript.getParameter("custscript_input_saved_search");

             let searchToExtract = search.load(savedSearchId)

                let searchColumns= searchToExtract.columns;
                let results=[];
                let pageData =searchToExtract.runPaged({pageSize:1000});
                let page = pageData.pageRanges;

                for(let k=0;k<page.length;k++)
                {
                    let data = pageData.fetch({index:k}).data;
                    for (let j = 0; j < data.length; j++){
                        let singleResult = JSON.parse('{}');
                        let result = data[j];
                        for(let i=0;i<searchColumns.length;i++)
                        {
                            let value = result.getText(searchColumns[i]);
                            value=value?value:result.getValue(searchColumns[i]);
                            singleResult[searchColumns[i].label] = value?value:'';
                        }
                        results.push(singleResult);

                    }
                }

                let csvContent  = jsonToCSV(results);

              if(csvContent=="NO_RESULTS")
              {
                // No need to push if no result
                log.debug("NO_RESULTS",new Date().toUTCString())
                return;
              }
              let response = createRequest(sharedkey,azureAccount,csvContent,fileName,storage);
              log.debug("response "+new Date().toUTCString(),response);

            }catch(err)
            {
              log.error("error@execute",err);
            }
        }

      const createRequest = (key,account,csvContent,fileName,storage)=>{
        let strTime = new Date().toUTCString();
        let string_params = {
          'verb': 'PUT',
          'Content-Encoding': '',
          'Content-Language': '',
          'Content-Length': csvContent.length,
          'Content-MD5': '',
          'Content-Type': 'text/csv',
          'Date': '',
          'If-Modified-Since': '',
          'If-Match': '',
          'If-None-Match': '',
          'If-Unmodified-Since': '',
          'Range': '',
          'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob\nx-ms-date:' + strTime + '\nx-ms-version:' + '2020-04-08\n',
          'CanonicalizedResource':`/${account}/${storage}/${fileName}`
        }
        let strToSign = `${string_params['verb']}\n${string_params['Content-Encoding']}\n${string_params['Content-Language']}\n${string_params['Content-Length']}\n${string_params['Content-MD5']}\n${string_params['Content-Type']}\n${string_params['Date']}\n${string_params['If-Modified-Since']}\n${string_params['If-Match']}\n${string_params['If-None-Match']}\n${string_params['If-Unmodified-Since']}\n${string_params['Range']}\n${string_params['CanonicalizedHeaders']}${string_params['CanonicalizedResource']}`
        let secret = CryptoJS.enc.Base64.parse(key);
        let hash = CryptoJS.HmacSHA256(strToSign, secret);
        let hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
        let auth = `SharedKey ${account}:` + hashInBase64;

        const options = {
          url: `https://${account}.blob.core.windows.net/${storage}/${fileName}`,
          headers: {
            Authorization: auth,
            'x-ms-blob-type': 'BlockBlob',
            "x-ms-date": strTime,
            "x-ms-version": "2020-04-08",
            'Content-Type': "text/csv",
            'Content-Length': csvContent.length
          },
          body: csvContent
        };

        return https.put(options)

      }

        const jsonToCSV = (objArray) => {

            let array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
            let str = '';

            if(array.length==0) {
              return "NO_RESULTS";
            }
            let labels = '';
            for(let index in array[0])
            {
                labels += formatData(index) + ','
            }
            labels.slice(0, labels.Length - 1);

            str += labels + '\r\n';

            for (let i = 0; i < array.length; i++) {
                let line = '';

                for (let index in array[i]) {
                    line += formatData(array[i][index]) + ',';
                }

                line.slice(0, line.Length - 1);

                str += line + '\r\n';
            }
            return str;
        }

        const formatData = (str)=>
        {
            return str?'\"'+str+'\"':'';
        }

      /*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
      var CryptoJS=CryptoJS||function(h,r){var k={},l=k.lib={},n=function(){},f=l.Base={extend:function(a){n.prototype=this;var b=new n;a&&b.mixIn(a);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
            j=l.WordArray=f.extend({init:function(a,b){a=this.words=a||[];this.sigBytes=b!=r?b:4*a.length},toString:function(a){return(a||s).stringify(this)},concat:function(a){var b=this.words,d=a.words,c=this.sigBytes;a=a.sigBytes;this.clamp();if(c%4)for(var e=0;e<a;e++)b[c+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((c+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)b[c+e>>>2]=d[e>>>2];else b.push.apply(b,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<
                    32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*h.random()|0);return new j.init(b,a)}}),m=k.enc={},s=m.Hex={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,
                    2),16)<<24-4*(c%8);return new j.init(d,b/2)}},p=m.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return new j.init(d,b)}},t=m.Utf8={stringify:function(a){try{return decodeURIComponent(escape(p.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return p.parse(unescape(encodeURIComponent(a)))}},
            q=l.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new j.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=t.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,f=c/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;c=h.min(4*a,c);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);b.sigBytes-=c}return new j.init(g,c)},clone:function(){var a=f.clone.call(this);
                a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:f.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new u.HMAC.init(a,
              d)).finalize(b)}}});var u=k.algo={};return k}(Math);

      /*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
      (function(){var c=CryptoJS,k=c.enc.Utf8;c.algo.HMAC=c.lib.Base.extend({init:function(a,b){a=this._hasher=new a.init;"string"==typeof b&&(b=k.parse(b));var c=a.blockSize,e=4*c;b.sigBytes>e&&(b=a.finalize(b));b.clamp();for(var f=this._oKey=b.clone(),g=this._iKey=b.clone(),h=f.words,j=g.words,d=0;d<c;d++)h[d]^=1549556828,j[d]^=909522486;f.sigBytes=g.sigBytes=e;this.reset()},reset:function(){var a=this._hasher;a.reset();a.update(this._iKey)},update:function(a){this._hasher.update(a);return this},finalize:function(a){var b=
              this._hasher;a=b.finalize(a);b.reset();return b.finalize(this._oKey.clone().concat(a))}})})();

      /*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
HMAC256
*/
      (function(k){for(var g=CryptoJS,h=g.lib,v=h.WordArray,j=h.Hasher,h=g.algo,s=[],t=[],u=function(q){return 4294967296*(q-(q|0))|0},l=2,b=0;64>b;){var d;a:{d=l;for(var w=k.sqrt(d),r=2;r<=w;r++)if(!(d%r)){d=!1;break a}d=!0}d&&(8>b&&(s[b]=u(k.pow(l,0.5))),t[b]=u(k.pow(l,1/3)),b++);l++}var n=[],h=h.SHA256=j.extend({_doReset:function(){this._hash=new v.init(s.slice(0))},_doProcessBlock:function(q,h){for(var a=this._hash.words,c=a[0],d=a[1],b=a[2],k=a[3],f=a[4],g=a[5],j=a[6],l=a[7],e=0;64>e;e++){if(16>e)n[e]=
              q[h+e]|0;else{var m=n[e-15],p=n[e-2];n[e]=((m<<25|m>>>7)^(m<<14|m>>>18)^m>>>3)+n[e-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+n[e-16]}m=l+((f<<26|f>>>6)^(f<<21|f>>>11)^(f<<7|f>>>25))+(f&g^~f&j)+t[e]+n[e];p=((c<<30|c>>>2)^(c<<19|c>>>13)^(c<<10|c>>>22))+(c&d^c&b^d&b);l=j;j=g;g=f;f=k+m|0;k=b;b=d;d=c;c=m+p|0}a[0]=a[0]+c|0;a[1]=a[1]+d|0;a[2]=a[2]+b|0;a[3]=a[3]+k|0;a[4]=a[4]+f|0;a[5]=a[5]+g|0;a[6]=a[6]+j|0;a[7]=a[7]+l|0},_doFinalize:function(){var d=this._data,b=d.words,a=8*this._nDataBytes,c=8*d.sigBytes;
          b[c>>>5]|=128<<24-c%32;b[(c+64>>>9<<4)+14]=k.floor(a/4294967296);b[(c+64>>>9<<4)+15]=a;d.sigBytes=4*b.length;this._process();return this._hash},clone:function(){var b=j.clone.call(this);b._hash=this._hash.clone();return b}});g.SHA256=j._createHelper(h);g.HmacSHA256=j._createHmacHelper(h)})(Math);

        /*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
Base 64
*/
      (function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a<f;a+=3)for(var d=(e[a>>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g<f;g++)b.push(c.charAt(d>>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d<
        e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();



        return {execute}

    });
<scheduledscript scriptid="customscript_adf_hms_dispatch_push">
  <description></description>
  <isinactive>F</isinactive>
  <name>ADF Push Dispatch Data to Azure</name>
  <notifyadmins>F</notifyadmins>
  <notifyemails></notifyemails>
  <notifyowner>T</notifyowner>
  <scriptfile>[/SuiteScripts/ADF push Dispatch Data to azure.js]</scriptfile>
  <scriptcustomfields>
    <scriptcustomfield scriptid="custscript_azure_shared_key">
      <accesslevel>2</accesslevel>
      <applyformatting>F</applyformatting>
      <checkspelling>F</checkspelling>
      <defaultchecked>F</defaultchecked>
      <defaultselection></defaultselection>
      <defaultvalue></defaultvalue>
      <description></description>
      <displayheight></displayheight>
      <displaytype>NORMAL</displaytype>
      <displaywidth></displaywidth>
      <dynamicdefault></dynamicdefault>
      <fieldtype>TEXT</fieldtype>
      <help></help>
      <isformula>F</isformula>
      <ismandatory>F</ismandatory>
      <label>Shared Key</label>
      <linktext></linktext>
      <maxlength></maxlength>
      <maxvalue></maxvalue>
      <minvalue></minvalue>
      <onparentdelete></onparentdelete>
      <searchlevel>2</searchlevel>
      <selectrecordtype></selectrecordtype>
      <setting></setting>
      <storevalue>T</storevalue>
    </scriptcustomfield>
    <scriptcustomfield scriptid="custscript_azure_account">
      <accesslevel>2</accesslevel>
      <applyformatting>F</applyformatting>
      <checkspelling>F</checkspelling>
      <defaultchecked>F</defaultchecked>
      <defaultselection></defaultselection>
      <defaultvalue></defaultvalue>
      <description></description>
      <displayheight></displayheight>
      <displaytype>NORMAL</displaytype>
      <displaywidth></displaywidth>
      <dynamicdefault></dynamicdefault>
      <fieldtype>TEXT</fieldtype>
      <help></help>
      <isformula>F</isformula>
      <ismandatory>F</ismandatory>
      <label>Account</label>
      <linktext></linktext>
      <maxlength></maxlength>
      <maxvalue></maxvalue>
      <minvalue></minvalue>
      <onparentdelete></onparentdelete>
      <searchlevel>2</searchlevel>
      <selectrecordtype></selectrecordtype>
      <setting></setting>
      <storevalue>T</storevalue>
    </scriptcustomfield>
    <scriptcustomfield scriptid="custscript_azure_storage">
      <accesslevel>2</accesslevel>
      <applyformatting>F</applyformatting>
      <checkspelling>F</checkspelling>
      <defaultchecked>F</defaultchecked>
      <defaultselection></defaultselection>
      <defaultvalue></defaultvalue>
      <description></description>
      <displayheight></displayheight>
      <displaytype>NORMAL</displaytype>
      <displaywidth></displaywidth>
      <dynamicdefault></dynamicdefault>
      <fieldtype>TEXT</fieldtype>
      <help></help>
      <isformula>F</isformula>
      <ismandatory>F</ismandatory>
      <label>Storage</label>
      <linktext></linktext>
      <maxlength></maxlength>
      <maxvalue></maxvalue>
      <minvalue></minvalue>
      <onparentdelete></onparentdelete>
      <searchlevel>2</searchlevel>
      <selectrecordtype></selectrecordtype>
      <setting></setting>
      <storevalue>T</storevalue>
    </scriptcustomfield>
    <scriptcustomfield scriptid="custscript_azure_file_name">
      <accesslevel>2</accesslevel>
      <applyformatting>F</applyformatting>
      <checkspelling>F</checkspelling>
      <defaultchecked>F</defaultchecked>
      <defaultselection></defaultselection>
      <defaultvalue></defaultvalue>
      <description></description>
      <displayheight></displayheight>
      <displaytype>NORMAL</displaytype>
      <displaywidth></displaywidth>
      <dynamicdefault></dynamicdefault>
      <fieldtype>TEXT</fieldtype>
      <help></help>
      <isformula>F</isformula>
      <ismandatory>F</ismandatory>
      <label>File Name</label>
      <linktext></linktext>
      <maxlength></maxlength>
      <maxvalue></maxvalue>
      <minvalue></minvalue>
      <onparentdelete></onparentdelete>
      <searchlevel>2</searchlevel>
      <selectrecordtype></selectrecordtype>
      <setting></setting>
      <storevalue>T</storevalue>
    </scriptcustomfield>
    <scriptcustomfield scriptid="custscript_input_saved_search">
      <accesslevel>2</accesslevel>
      <applyformatting>F</applyformatting>
      <checkspelling>F</checkspelling>
      <defaultchecked>F</defaultchecked>
      <defaultselection></defaultselection>
      <defaultvalue></defaultvalue>
      <description></description>
      <displayheight></displayheight>
      <displaytype>NORMAL</displaytype>
      <displaywidth></displaywidth>
      <dynamicdefault></dynamicdefault>
      <fieldtype>SELECT</fieldtype>
      <help></help>
      <isformula>F</isformula>
      <ismandatory>F</ismandatory>
      <label>Saved Search</label>
      <linktext></linktext>
      <maxlength></maxlength>
      <maxvalue></maxvalue>
      <minvalue></minvalue>
      <onparentdelete>NO_ACTION</onparentdelete>
      <searchlevel>2</searchlevel>
      <selectrecordtype>-119</selectrecordtype>
      <setting></setting>
      <storevalue>T</storevalue>
    </scriptcustomfield>
  </scriptcustomfields>
  <scriptdeployments>
    <scriptdeployment scriptid="customdeploy_adf_hms_dispatch_push_week">
      <custscript_azure_account>hmsdatalakestoragedev</custscript_azure_account>
      <custscript_azure_file_name>dispatchData</custscript_azure_file_name>
      <custscript_azure_shared_key>36FhkLFQTFeipTIlWXdN/yHyYn98W4fx9dCCgFAdj05hytQW2wI7dOn9xA0XmzgdM/ICT06wZyCxxI2k+NYzgQ==</custscript_azure_shared_key>
      <custscript_azure_storage>hmsdatalakefilesystemdev</custscript_azure_storage>
      <custscript_input_saved_search>[scriptid=customsearch2394]</custscript_input_saved_search>
      <isdeployed>T</isdeployed>
      <loglevel>DEBUG</loglevel>
      <status>NOTSCHEDULED</status>
      <title>ADF Push Dispatch Data to Azure - Weekly (Whole data)</title>
      <recurrence>
        <single>
          <repeat></repeat>
          <startdate>2021-08-06</startdate>
          <starttime>12:30:00Z</starttime>
        </single>
      </recurrence>
    </scriptdeployment>
    <scriptdeployment scriptid="customdeploy_adf_hms_dispatch_push">
      <custscript_azure_account>hmsdatalakestoragedev</custscript_azure_account>
      <custscript_azure_file_name>dispatchData</custscript_azure_file_name>
      <custscript_azure_shared_key>36FhkLFQTFeipTIlWXdN/yHyYn98W4fx9dCCgFAdj05hytQW2wI7dOn9xA0XmzgdM/ICT06wZyCxxI2k+NYzgQ==</custscript_azure_shared_key>
      <custscript_azure_storage>hmsdatalakefilesystemdev</custscript_azure_storage>
      <custscript_input_saved_search>[scriptid=customsearch2394]</custscript_input_saved_search>
      <isdeployed>T</isdeployed>
      <loglevel>DEBUG</loglevel>
      <status>NOTSCHEDULED</status>
      <title>ADF Push Dispatch Data to Azure</title>
      <recurrence>
        <single>
          <repeat></repeat>
          <startdate>2021-08-06</startdate>
          <starttime>12:30:00Z</starttime>
        </single>
      </recurrence>
    </scriptdeployment>
  </scriptdeployments>
</scheduledscript>

Azure data lake integration Sends NetSuite report files to Azure storage using blob API

Leave a comment

Your email address will not be published. Required fields are marked *