3
\$\begingroup\$

The main entry point is the SyncmyCompany() function.

I have a interesting scenario where I'm syncing data from a Company to a destination system. In this case Dynamics 365 is the destination system. The main flow of the code is:

  1. Get the configuration defined in Dynamics 365 and store data in my configuration object.
  2. Only once complete step 1, pass in company user creds to get a sts token I used in the company's api calls and store it in my configuration object.
  3. Only once complete step 2, get task from the company api.
  4. Only once complete step 3, get task that exists in Dynamics 365 if any.
  5. Only once complete step 4, if there are task in Dynamics 365 delete them using the task id and Xrm WebApi helper delete method. This requires some behind the scenes deletion of sdk message and task trigger dependencies too.
  6. Only once complete step 5, use the Xrm WebApi helper create method to repopulate the dropped task data with new data from the source company.

I am using Promises in order to make this possible.

What changes can I make to my Promise logic to make this cleaner and have a stronger order enforced in my code?

Code

"use strict";

var MyCompanyDynamicsClientApi = MyCompanyDynamicsClientApi || {};

MyCompanyDynamicsClientApi.configuration = {
  clientUrl: "",
  MyCompanyApiUrl: "",
  MyCompanyChallengeHostEmail: "",
  MyCompanyChallengeHostPassword: "",
  MyCompanyChallengeId: "",
  MyCompanyChallengeTasks: [],
  MyCompanyChallengeTasksInDynamics: {},
  MyCompanyClientSecret: "",
  MyCompanyUsername: "",
  MyCompanyPassword: "",
  stsToken: ""
}

MyCompanyDynamicsClientApi.runSyncMyCompanyProcess = function() {


  Xrm.Utility.showProgressIndicator("Syncing MyCompany Data...");
  MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration().then(function () {
    return MyCompanyDynamicsClientApi.getMyCompanyTokenAsync();
  }).then(function () {
    return MyCompanyDynamicsClientApi.getMyCompanyTasksAsync();
  }).then(function () {
    return MyCompanyDynamicsClientApi.getMyCompanyTasksInDynamicsAsync();
  }).then(function () {
    return MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync();
  }).then(function () {
    return MyCompanyDynamicsClientApi.createMyCompanyTasksAsync();
  }).then(function () {
    return MyCompanyDynamicsClientApi.openSyncMyCompanyCompleteDialog();
  }).catch(function (error) {
    return MyCompanyDynamicsClientApi.openSyncMyCompanyFailedDialog(error());
  });
}

MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration = function() {
  console.log("Trying Promise for MyCompanyConfiguration");
  return new Promise(function(resolve, reject) {
    MyCompanyDynamicsClientApi.configuration.clientUrl = window.parent.Xrm.Utility.getGlobalContext().getClientUrl();

    var MyCompanyConfigurationFetchXml = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true" no-lock="false">';
    MyCompanyConfigurationFetchXml = MyCompanyConfigurationFetchXml.concat(
        '<entity name="MyCompany_MyCompanyconfiguration">',
          '<attribute name="MyCompany_apiurl" />',
          '<attribute name="MyCompany_challengehostemail" />',
          '<attribute name="MyCompany_challengehostpassword" />',
          '<attribute name="MyCompany_clientsecret" />',
          '<attribute name="MyCompany_MyCompanyconfigurationid" />',
          '<filter type="and">',
            '<condition attribute="MyCompany_apiurl" operator="not-null" />',
            '<condition attribute="MyCompany_challengehostemail" operator="not-null" />',
            '<condition attribute="MyCompany_challengehostpassword" operator="not-null" />',
            '<condition attribute="MyCompany_clientsecret" operator="not-null" />',
            '<condition attribute="statecode" operator="eq" value="0" />',
          '</filter>',
          '<link-entity name="MyCompany_MyCompanychallenge" from="MyCompany_MyCompanyconfigurationid" to="MyCompany_MyCompanyconfigurationid" link-type="inner" alias="MyCompany_MyCompanychallenge">',
            '<attribute name="MyCompany_MyCompanyid" />',
            '<attribute name="MyCompany_name" />',
            '<attribute name="MyCompany_MyCompanyconfigurationid" />',
            '<filter type="and">',
              '<condition attribute="MyCompany_MyCompanyid" operator="not-null" />',
              '<condition attribute="MyCompany_name" operator="not-null" />',
              '<condition attribute="MyCompany_MyCompanyconfigurationid" operator="not-null" />',
              '<condition attribute="statecode" operator="eq" value="0" />',
            '</filter>',
          '</link-entity>',
        '</entity>',
      '</fetch>'
    );
    var MyCompanyConfigurationFetchXmlEncoded = encodeURI(MyCompanyConfigurationFetchXml);
    var MyCompanyConfigurationRequestPath = "";
    MyCompanyConfigurationRequestPath = MyCompanyConfigurationRequestPath.concat(
      MyCompanyDynamicsClientApi.configuration.clientUrl,
      "/api/data/v9.0/MyCompany_MyCompanyconfigurations?fetchXml=",
      MyCompanyConfigurationFetchXmlEncoded
    );

    MyCompanyDynamicsClientApi.request("GET", MyCompanyConfigurationRequestPath, "application/json").then(function (response) {
      try {
        MyCompanyDynamicsClientApi.configuration.MyCompanyApiUrl = response.value[0].MyCompany_apiurl.toString();
        MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeHostEmail = response.value[0].MyCompany_challengehostemail.toString();
        MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeHostPassword = response.value[0].MyCompany_challengehostpassword.toString();
        MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeId = response.value[0]["MyCompany_MyCompanychallenge.MyCompany_MyCompanyid"].toString();
        MyCompanyDynamicsClientApi.configuration.MyCompanyClientSecret = response.value[0].MyCompany_clientsecret.toString();
        resolve();
      } catch (error) { reject(new Error("Failed Promise MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration" + error.message)); }
    }).catch(function(error) { reject(new Error("Failed Promise MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration" + error.message)); });
  });
}
MyCompanyDynamicsClientApi.getMyCompanyTokenAsync = function() {
  console.log("Trying Promise for MyCompanyTokenAsync");
  return new Promise(function(resolve, reject) {

    var stsToken = "";
    var tokenRequestParams = "grant_type=password&scope=MyCompanyApi&username=" + MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeHostEmail + "&password=" + MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeHostPassword;
    var tokenRequestHeaders = { "Authorization": "Basic TWljcm9zb2Z0RmxvdzI6W0k0JjUoXVtPZUJsTk4laGduIXo5cH1TMmp5Wlt4Xmk5" };
    var stsTokenRequestUri = "https://playMyCompanydev.azurewebsites.net/sts/connect/token";
    MyCompanyDynamicsClientApi.request("POST", stsTokenRequestUri, "application/x-www-form-urlencoded", tokenRequestHeaders, tokenRequestParams).then(function (response) {
      try {
        MyCompanyDynamicsClientApi.configuration.stsToken = response.access_token.toString();
        resolve();
      } catch (error) { reject(new Error("Failed Promise MyCompanyDynamicsClientApi.getMyCompanyTokenAsync" + error.message)); }
    }).catch(function(error) { reject(new Error("Failed Promise MyCompanyDynamicsClientApi.getMyCompanyTokenAsync" + error.message)); });
  });
}
MyCompanyDynamicsClientApi.getMyCompanyTasksAsync = function() {
  console.log("Trying Promise for getMyCompanyTasksAsync");
  return new Promise(function(resolve, reject) {

    var taskRequestHeaders = { "Authorization": "Bearer " + MyCompanyDynamicsClientApi.configuration.stsToken };
    var taskRequestUri = MyCompanyDynamicsClientApi.configuration.MyCompanyApiUrl + "dynamics/" + MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeId + "/tasks";
    MyCompanyDynamicsClientApi.request("GET", taskRequestUri, "application/json", taskRequestHeaders).then(function (response) {
      try {
        MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks = response;
        resolve();
      } catch (error) { reject(new Error("Failed Promise MyCompanyDynamicsClientApi.getMyCompanyTasksAsync" + error.message)); }
    }).catch(function(error) { reject(new Error("Failed Promise MyCompanyDynamicsClientApi.getMyCompanyTasksAsync" + error.message)); });
  });
}
MyCompanyDynamicsClientApi.getMyCompanyTasksInDynamicsAsync = function() {
  console.log("Trying Promise for getMyCompanyTasksInDynamicsAsync");
  return new Promise(function(resolve, reject) {

    var MyCompanyTasksFetchXml = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true" no-lock="false">';
    MyCompanyTasksFetchXml = MyCompanyTasksFetchXml.concat(
        '<entity name="MyCompany_MyCompanytask">',
          '<attribute name="MyCompany_MyCompanytaskid" />',
          '<attribute name="MyCompany_MyCompanytaskidreference" />',
          '<attribute name="MyCompany_sdkmessagestepid" />',
          '<filter type="and">',
            '<condition attribute="MyCompany_MyCompanytaskid" operator="not-null" />',
            '<condition attribute="MyCompany_MyCompanytaskidreference" operator="not-null" />',
            '<condition attribute="MyCompany_sdkmessagestepid" operator="not-null" />',
          '</filter>',
          '<link-entity name="MyCompany_MyCompanytrigger" from="MyCompany_MyCompanytriggerid" to="MyCompany_MyCompanytriggerid" link-type="inner" alias="MyCompany_MyCompanytrigger" >',
            '<attribute name="MyCompany_MyCompanytriggerid" />',
            '<filter type="and">',
              '<condition attribute="MyCompany_MyCompanytriggerid" operator="not-null" />',
            '</filter>',
          '</link-entity>',
        '</entity>',
      '</fetch>'
    );
    // TODO: Add filter to fetch xml to merge task instead of just drop and recreate table
    // '<condition attribute="MyCompany_apiurl" operator="not-null" />',
    // '<filter type="and">',
    //     '<condition attribute="MyCompany_MyCompanytaskidreference" operator="eq" value="' + MyCompanyTaskIdReference + '" />',
    // '</filter>',
    var MyCompanyTasksFetchXmlEncoded = encodeURI(MyCompanyTasksFetchXml);
    var MyCompanyTasksRequestPath = "";
    MyCompanyTasksRequestPath = MyCompanyTasksRequestPath.concat(
      MyCompanyDynamicsClientApi.configuration.clientUrl,
      "/api/data/v9.0/MyCompany_MyCompanytasks?fetchXml=",
      MyCompanyTasksFetchXmlEncoded
    );
    MyCompanyDynamicsClientApi.request("GET", MyCompanyTasksRequestPath, "application/json").then(function (response) {
      try {
        MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics = response;
        resolve();
      } catch (error) { }
    }).catch(function(error) { });
  });
}
MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync = function() {
  console.log("Trying Promise for deleteMyCompanyTasksAsync");
  return new Promise(function(resolve, reject) {

    var deleteSdkMessage = function (sdkmessagestepid) {
      return new Promise(function(resolve, reject) {
        if (sdkmessagestepid) {
          Xrm.WebApi.deleteRecord("sdkmessageprocessingstep", sdkmessagestepid).then(
            function success(result) {
              console.log("Deleted sdkmessageprocessingstep " + result);
              resolve();
            },
            function (error) { console.log("ERROR: Xrm.WebApi.deleteRecord " + error.message.toString()); }
          ).catch(function(error) {
            reject(new Error("Failed Promise MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync " + error.message));
          });
        } else {
          resolve();
        }
      });
    };

    var deleteMyCompanyTrigger = function (triggerid) {
      return new Promise(function(resolve, reject) {
        if (triggerid) {
          Xrm.WebApi.deleteRecord("MyCompany_MyCompanytrigger", triggerid).then(
            function success(result) {
              console.log("Deleted MyCompany_MyCompanytrigger " + result);
              resolve();
            },
            function (error) { console.log("ERROR: Xrm.WebApi.deleteRecord " + error.message.toString()); }
          ).catch(function(error) {
            reject(new Error("Failed Promise MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync " + error.message));
          });
        } else {
          resolve();
        }
      });
    };

    var deleteMyCompanyTask = function (taskid) {
      return new Promise(function(resolve, reject) {
        if (taskid) {
          Xrm.WebApi.deleteRecord("MyCompany_MyCompanytask", taskid).then(
            function success(result) {
              console.log("Deleted MyCompany_MyCompanytask " + result);
              resolve();
            },
            function (error) { console.log("ERROR: Xrm.WebApi.deleteRecord " + error.message.toString()); }
          ).catch(function(error) {
            reject(new Error("Failed Promise MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync " + error.message));
          });
        } else {
          resolve();
        }
      });
    };

    var deleteMyCompanyTasksAsyncPromises = [];
    var MyCompanytaskid = "";
    var MyCompanysdkmessagestepid = "";
    var MyCompanytriggerid = "";
    for (var i = 0; i < MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value.length; i++) {

      if (MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value[i]) {
        MyCompanysdkmessagestepid = MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value[i].MyCompany_sdkmessagestepid.toString();
        deleteMyCompanyTasksAsyncPromises.push( deleteSdkMessage(MyCompanysdkmessagestepid) );
      }
      if (MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value[i]["MyCompany_MyCompanytrigger.MyCompany_MyCompanytriggerid"]) {
        MyCompanytriggerid = MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value[i]["MyCompany_MyCompanytrigger.MyCompany_MyCompanytriggerid"].toString();
        deleteMyCompanyTasksAsyncPromises.push( deleteMyCompanyTrigger(MyCompanytriggerid) );
      }
      if (MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value[i].MyCompany_MyCompanytaskid) {
        MyCompanytaskid = MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasksInDynamics.value[i].MyCompany_MyCompanytaskid.toString();
        deleteMyCompanyTasksAsyncPromises.push( deleteMyCompanyTask(MyCompanytaskid) );
      }
    }
    Promise.all(deleteMyCompanyTasksAsyncPromises).then(function() {
      resolve();
    });
  });
}
MyCompanyDynamicsClientApi.createMyCompanyTasksAsync = function() {
  console.log("Trying Promise for createMyCompanyTasksAsync");
  return new Promise(function(resolve, reject) {

    var createMyCompanyTask = function (trigger) {
      return new Promise(function(resolve, reject) {
        if (trigger) {
          Xrm.WebApi.createRecord("MyCompany_MyCompanytrigger", trigger).then(
            function success(result) {
              console.log("Created task and attached trigger to it, trigger is " + MyCompanyTrigger);
              resolve();
            },
            function (error) {
              console.log("ERROR: Xrm.WebApi.createRecord " + error.message.toString());
            }
          ).catch(function(error) {
            reject(new Error("Failed Promise MyCompanyDynamicsClientApi.createMyCompanyTasksAsync" + error.message));
          });
        } else {
          resolve();
        }
      });
    };

    var createMyCompanyTasksAsyncPromises = [];
    var MyCompanyTrigger = { };
    var MyCompanyEntityName = "";
    for (var i = 0; i < MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks.length; i++) {

      MyCompanyEntityName = MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].primaryobjecttypecode[0].toUpperCase() + MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].primaryobjecttypecode.slice(1);
      MyCompanyTrigger =
      {
        "MyCompany_name": MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].name + " " + MyCompanyEntityName,
        "MyCompany_entityname": MyCompanyEntityName,
        "MyCompany_messagename": MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].name,
        // MyCompany_MyCompany_MyCompanytask_MyCompany_MyCompanytrigger navigation property (1:N), from DataModel/MyCompanyCrmSdkTypes, generated class using the CrmSvcUtil.exe
        "MyCompany_MyCompany_MyCompanytrigger_MyCompany_MyCompanytask_MyCompanytriggerid":
        [
          {
            "MyCompany_name": MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].Title,
            "MyCompany_MyCompanytaskidreference": MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].TaskId,
            "MyCompany_enabled": true,
            "MyCompany_pointvalue": MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeTasks[i].EligiblePoints
          }
        ]
      };
      createMyCompanyTasksAsyncPromises.push( createMyCompanyTask(MyCompanyTrigger) );
    }

    Promise.all(createMyCompanyTasksAsyncPromises).then(function() {
      resolve();
    });
  });
}

MyCompanyDynamicsClientApi.request = function(method, uri, contentType, otherHeaders, data) {

  if (!RegExp(method, "g").test("POST PATCH PUT GET DELETE")) {
    throw new Error("MyCompanyDynamicsClientApi.request: method must be: " +
    "POST, PATCH, PUT, GET, or DELETE.");
  }
  if (!typeof uri === "string" || !typeof contentType === "string") {
    throw new Error("MyCompanyDynamicsClientApi.request: uri or contentType is not a string.");
  }
  if ((RegExp(method, "g").test("POST PATCH PUT")) && (!data)) {
    throw new Error("MyCompanyDynamicsClientApi.request: data must contain data for operations that create or modify data.");
  }

  return new Promise(function (resolve, reject) {
    var request = new XMLHttpRequest();
    request.open(method, encodeURI(uri), true);
    request.setRequestHeader("Content-Type", contentType);
    request.setRequestHeader("Cache-Control", "no-cache");
    request.onreadystatechange = function () {
      if (this.readyState === 4 && this.status === 200) {
        var response = JSON.parse(this.responseText);
        resolve(response);
      }
    };
    if (otherHeaders) {
      var value = "";
      for (var header in otherHeaders) {
        if (otherHeaders.hasOwnProperty(header)) {
          value = otherHeaders[header];
          request.setRequestHeader(header, value);
        }
      }
    }
    if (data) { request.send(data); }
    else { request.send(); }
  });
}

MyCompanyDynamicsClientApi.openSyncMyCompanyConfirmDialog = function() {

  console.log("Opening confirm dialog...");
  var confirmStrings = {
    cancelButtonLabel: "No",
    confirmButtonLabel: "Yes",
    title: "MyCompany Sync",
    subtitle: "Are you sure you want to start a sync?"
  };
  var confirmOptions = { height: 200, width: 450 };

  Xrm.Navigation.openConfirmDialog(confirmStrings, confirmOptions).then(
    success => {
      if (success.confirmed) {
        console.log("openSyncMyCompanyConfirmDialog confirmed.");
        MyCompanyDynamicsClientApi.runSyncMyCompanyProcess();
      }
      else {
        console.log("openSyncMyCompanyConfirmDialog closed.");
      }
    },
    error => {
      console.log(error.message);
  });
}
MyCompanyDynamicsClientApi.openSyncMyCompanyCompleteDialog = function() {
  return new Promise(function(resolve, reject) {
    Xrm.Utility.closeProgressIndicator();
    var alertStrings = {
      confirmButtonLabel: "Close",
      text: "Sync Complete! You can safely close this window."
    };
    var alertOptions = { height: 120, width: 260 };
    Xrm.Navigation.openAlertDialog(alertStrings, alertOptions).then(
      function success(result) {
        resolve();
        console.log("openSyncMyCompanyCompleteDialog dialog closed");
      },
      function (error) { console.log(error.message); }
    );
  });
}
MyCompanyDynamicsClientApi.openSyncMyCompanyFailedDialog = function(error) {
  return new Promise(function(resolve, reject) {
    Xrm.Utility.closeProgressIndicator();
    var alertStrings = {
      confirmButtonLabel: "Close",
      text: "ERROR :( the sync failed... please contact your admin. Error details below\n" + error.toString()
    };
    var alertOptions = { height: 120, width: 260 };
    Xrm.Navigation.openAlertDialog(alertStrings, alertOptions).then(
      function success(result) {
        resolve();
        console.log("openSyncMyCompanyFailedDialog dialog closed");
      },
      function (error) { console.log(error.message); }
    );
  });
}

function SyncMyCompany() {

  console.log("SyncMyCompany running...");
  MyCompanyDynamicsClientApi.openSyncMyCompanyConfirmDialog();
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ updated question to call this out and highlight review of the syntax and structure of code. \$\endgroup\$
    – greg
    Commented Jun 8, 2018 at 18:47
  • \$\begingroup\$ completed edits \$\endgroup\$
    – greg
    Commented Jun 8, 2018 at 21:58

1 Answer 1

2
\$\begingroup\$

For how that code looks like, and based on all the methods being just namespaced within MyCompanyDynamicsClientApi, you could remove all the function() {} wraps between the calls, giving you something like:

MyCompanyDynamicsClientApi.runSyncMyCompanyProcess = function() {
Xrm.Utility.showProgressIndicator("Syncing MyCompany Data...");
MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration()
    .then(MyCompanyDynamicsClientApi.getMyCompanyTokenAsync)
    .then(MyCompanyDynamicsClientApi.getMyCompanyTasksAsync)
    .then(MyCompanyDynamicsClientApi.getMyCompanyTasksInDynamicsAsync)
    .then(MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync)
    .then(MyCompanyDynamicsClientApi.createMyCompanyTasksAsync)
    .then(MyCompanyDynamicsClientApi.openSyncMyCompanyCompleteDialog)
    .catch(function (error) {
        return MyCompanyDynamicsClientApi.openSyncMyCompanyFailedDialog(error());
    });
}

on top of that, I don't know why you are calling the error. It doesn't seem to be a function.

MyCompanyDynamicsClientApi.runSyncMyCompanyProcess = function() {
Xrm.Utility.showProgressIndicator("Syncing MyCompany Data...");
MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration()
    .then(MyCompanyDynamicsClientApi.getMyCompanyTokenAsync)
    .then(MyCompanyDynamicsClientApi.getMyCompanyTasksAsync)
    .then(MyCompanyDynamicsClientApi.getMyCompanyTasksInDynamicsAsync)
    .then(MyCompanyDynamicsClientApi.deleteMyCompanyTasksAsync)
    .then(MyCompanyDynamicsClientApi.createMyCompanyTasksAsync)
    .then(MyCompanyDynamicsClientApi.openSyncMyCompanyCompleteDialog)
    .catch(MyCompanyDynamicsClientApi.openSyncMyCompanyFailedDialog);
}

For most of your code you can just return the promise returned by .request, and throw in the other errors, because you are already in a Promise chain, if you throw an error it will reject the promise chain and trigger the catch.

MyCompanyDynamicsClientApi.getMyCompanyTokenAsync = function() {
    console.log("Trying Promise for MyCompanyTokenAsync");
    // Anything failing here will make the outer promise to fail and catch to be called
    var stsToken = "";
    var tokenRequestParams = "grant_type=password&scope=MyCompanyApi&username=" + MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeHostEmail + "&password=" + MyCompanyDynamicsClientApi.configuration.MyCompanyChallengeHostPassword;
    var tokenRequestHeaders = { "Authorization": "Basic TWljcm9zb2Z0RmxvdzI6W0k0JjUoXVtPZUJsTk4laGduIXo5cH1TMmp5Wlt4Xmk5" };
    var stsTokenRequestUri = "https://playMyCompanydev.azurewebsites.net/sts/connect/token";

    return MyCompanyDynamicsClientApi.request("POST", stsTokenRequestUri, "application/x-www-form-urlencoded", tokenRequestHeaders, tokenRequestParams)
        .then(function (response) {
            // Anything failing here will make this promise to fail and catch to be called
            MyCompanyDynamicsClientApi.configuration.stsToken = response.access_token.toString();
        })
        .catch(function(error) { throw new Error("Failed Promise MyCompanyDynamicsClientApi.getMyCompanyTokenAsync" + error.message); });
    });
}

Other stuff not necessarily related to Promises:

If this runs in node, consider using template literals: ``

var MyCompanyConfigurationFetchXml = `
    <fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true" no-lock="false">
    <entity name="MyCompany_MyCompanyconfiguration">
    <attribute name="MyCompany_apiurl" />
    <attribute name="MyCompany_challengehostemail" />
    <attribute name="MyCompany_challengehostpassword" />
    <attribute name="MyCompany_clientsecret" />
    <attribute name="MyCompany_MyCompanyconfigurationid" />
    <filter type="and">
        <condition attribute="MyCompany_apiurl" operator="not-null" />
        <condition attribute="MyCompany_challengehostemail" operator="not-null" />
        <condition attribute="MyCompany_challengehostpassword" operator="not-null" />
        <condition attribute="MyCompany_clientsecret" operator="not-null" />
        <condition attribute="statecode" operator="eq" value="0" />
    </filter>
    <link-entity name="MyCompany_MyCompanychallenge" from="MyCompany_MyCompanyconfigurationid" to="MyCompany_MyCompanyconfigurationid" link-type="inner" alias="MyCompany_MyCompanychallenge">
        <attribute name="MyCompany_MyCompanyid" />
        <attribute name="MyCompany_name" />
        <attribute name="MyCompany_MyCompanyconfigurationid" />
        <filter type="and">
        <condition attribute="MyCompany_MyCompanyid" operator="not-null" />
        <condition attribute="MyCompany_name" operator="not-null" />
        <condition attribute="MyCompany_MyCompanyconfigurationid" operator="not-null" />
        <condition attribute="statecode" operator="eq" value="0" />
        </filter>
    </link-entity>
    </entity>
</fetch>`;

Extracting Constants and Mappers to separate methods:

function mapValues(response) {
    var values = response.values[0];

    return {
        MyCompanyApiUrl: values.MyCompany_apiurl.toString(),
        MyCompanyChallengeHostEmail: values.MyCompany_challengehostemail.toString(),
        MyCompanyChallengeHostPassword: values.MyCompany_challengehostpassword.toString(),
        MyCompanyChallengeId: values["MyCompany_MyCompanychallenge.MyCompany_MyCompanyid"].toString(),
        MyCompanyClientSecret: values.MyCompany_clientsecret.toString(),
    };
}

MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration = function() {
    console.log("Trying Promise for MyCompanyConfiguration");
    MyCompanyDynamicsClientApi.configuration.clientUrl = window.parent.Xrm.Utility.getGlobalContext().getClientUrl();

    var requestPath = getCompanyConfigurationRequestPath(MyCompanyDynamicsClientApi.configuration.clientUrl);

    MyCompanyDynamicsClientApi.request("GET", requestPath, "application/json")
        .then(function(response) {
            Object.assign(MyCompanyDynamicsClientApi.configuration, mapValues(response));
        })
        .catch(function(error) { 
            throw new Error("Failed Promise MyCompanyDynamicsClientApi.retrieveMyCompanyConfiguration" + error.message); 
        });
    });
}

You might also want to split this in separate files or/and encapsulate things better so you can rely less on namespaces. It would probably make the code more readable.

Regarding the order enforcement, promises should handle that properly. If you want to parallelise some work, you can also use Promise.all().

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

\$\endgroup\$
4
  • \$\begingroup\$ perfect, these are the suggestions I was looking for. I am considering using node for dev, using require/imports, then minimiaing all of my code into one Main.js file. Is this possible? In production the platform will only accept pure js in a single file. \$\endgroup\$
    – greg
    Commented Jun 21, 2018 at 21:46
  • \$\begingroup\$ also, what is your opinion on name spacing in the way I have done here? I want to make sure my library is unique for the partners I work with. \$\endgroup\$
    – greg
    Commented Jun 22, 2018 at 0:46
  • 1
    \$\begingroup\$ The namespacing you did is ok, but you should consider to only expose the public interface of your module. If all users can call is one method, make that public and keep everything else out of the namespace. You could consider using a bundler like webpack or rollup, it will help with the encapsulation and testability. \$\endgroup\$ Commented Jun 22, 2018 at 1:59
  • \$\begingroup\$ awesome, this was really helpful. going to start refactoring my projects right now! \$\endgroup\$
    – greg
    Commented Jun 22, 2018 at 2:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.