Git Product home page Git Product logo

dynamicswebapi's People

Contributors

03-ciprianog avatar aleksandrrogov avatar andreavalenzi avatar azakariamsft avatar benlaughlin avatar dependabot[bot] avatar grantorin avatar kukunin avatar mfradcourt avatar ncjones avatar yndal avatar yonaichin avatar zectbynmo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dynamicswebapi's Issues

Alternate keys does not allow to use integer value (it is forcing to use a string)

The current validation in the keyParameterCheck(...) function is forcing the key parameters to be of type string but it may happen that an integer is used.

For example, when a field is a drop down in Dynamics, the value behind is an integer.
var key = "xxx='123456',yyy='137891',select_option=959600000"

The Dynamics CRM API is expecting to receive the value without single quote in case of an integer value.

I'm preparing a PR so no need to worry too much about this, just logging an issue so there is a trace about the feature/bug :-)

maxPageSize doesn't apply when using retrieveAllRequest()

Using the below code, all records are retrieved instead of only 5.

		crm.retrieveAllRequest({
			collection: req.params.entity,
			select: ["name"],
			filter: "statecode eq 0",
			maxPageSize: 5,
    		        count: true,
		}).then(function (records) {
			res.send(records);
		}).catch(function (error) {
			console.log(error.message);
			res.status(400).send({
				error: error.message
			});
		});

Unable to execute the Web API function as synchronous

Hello,

I tried creating a synchronous Web API request using your helper library but when I debug the code, it is not executing as a synchronous function.

My requirement is to create a function that retrieves and return the name of the security role from Dynamics CRM based on the GUID.

Do we have the support for synchronous Web API calls in the library?

If yes, then can you please provide me some sample code to execute the retrieve request in a synchronous manner?

Please refer the function code as below:

function getSecurityRoleName(roleId) {
  var roleName = null;
  if (!isBlankOrNull(roleId)) {
    var requestToGetSecurityRoleName = {
      key: roleId,
      collection: plural("role"),
      select: ["name"],
      //Indicates whether the requests should be made synchronously or asynchronously. Default value is true (asynchronously).
      async: false, //Synchronous
      //ETag value with the If-None-Match header to request data to be retrieved only 
      //if it has changed since the last time it was retrieved.
      ifnonematch: 'W/"468026"',
    };
    //Initialize a new instance of DynamicsWebApi with a configuration object
    var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: '9.0' });

    //Perform a retrieve operaion
    dynamicsWebApi.retrieveRequest(requestToGetSecurityRoleName).then(function (record) {
      if (record != null) {
        roleName = record["name"];  //On debug, this statement is executing after the return statement.
      }
    })
    .catch(function (error) {
    });
  }
  return roleName; //On debug, this statement is executing first before the retrieve call.
}

FetchXML + aggregate="true"

When using an aggregate function within the fetchxml statement it fails because paging information are added.

Thanks for the library. It really helpful.

Parse response error

Hi,

I get an JS error when I try the next code:

dynamicsWebApi.retrieveAll("accounts", ["name", "emailaddress1"]).then(function (response) {
    var records = response.value;
    console.log(records);
})
.catch(function (error) {
});
\dynamics-web-api\lib\requests\helpers\parseResponse.js:33
        if (object[keys[i]].constructor === Array) {

TypeError: Cannot read property 'constructor' of null

As a workaround I have changed line 33 of parseResponse.js, from:

if (object[keys[i]].constructor === Array) {

To:

if (object[keys[i]] != null && object[keys[i]].constructor === Array) {

Hope it helps!

PD: Seems that the error comes because there are accounts without email and calling ".constructor" of if.

WebResource in Dynamics 365

Hi,

I'm trying to use the file as a WebResource in Dynamics 365. I keep getting an error saying DynamicsWebApi is not defined when trying to create one (var x = new DynamicsWebApi();). It's been added to the form as a library.

Tried to use require.js and head,js to load it but got the same result...

I wasn't sure which file in the dist folder is the correct one so I tried with all of them with the same result.

This is with Dynamics 365 trial.

Thanks!

Fetching CSDL metadata document

Is there any way to fetch the CSDL metadata document using this library? Is there any way to send a raw http request instead of using the API's defined in the readme?

message: 'Unable to fetch EntityDefinitions. Error: connect ECONNREFUSED 127.0.0.1:443'

I cannot make any requests with this lib.

Code is below. My console.log on the adalCallback prints the bearer token completely and all the information looks good.

The error I get is

[10/12/18 12:06:04 AM] ERROR { Error: connect ECONNREFUSED 127.0.0.1:443
[10/12/18 12:06:04 AM]     at Object._errnoException (util.js:1024:11)
[10/12/18 12:06:04 AM]     at _exceptionWithHostPort (util.js:1046:20)
[10/12/18 12:06:04 AM]     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
[10/12/18 12:06:04 AM]   code: 'ECONNREFUSED',
[10/12/18 12:06:04 AM]   syscall: 'connect',
[10/12/18 12:06:04 AM]   address: '127.0.0.1',
[10/12/18 12:06:04 AM]   port: 443 }
var AuthenticationContext = require('adal-node').AuthenticationContext;
const secrets = require('../../secrets/secrets.json')
 
//the following settings should be taken from Azure for your application
//and stored in app settings file or in global variables
 
//OAuth Token Endpoint
var authorityUrl = 'https://login.microsoftonline.com/xxxxxxxxx/oauth2/token';
//CRM Organization URL
var resource = 'https://xxxxxx.crm.dynamics.com';
//Dynamics 365 Client Id when registered in Azure
var applicantionId = 'xxxxxxxxxxxxxxxx';
const username = process.env['CRM_USERNAME']
const password = process.env['CRM_PASSWORD']

var AuthenticationContext = require('adal-node').AuthenticationContext;
var adalContext = new AuthenticationContext(authorityUrl);


function acquireToken(dynamicsWebApiCallback) {
    console.log('getting token')
    function adalCallback(error, token) {
        if (!error) {
            console.log(token)
            dynamicsWebApiCallback(token);
        } else {
            console.log('error token', error)
        }
    }
    console.log('resource, username, password, applicantionId', resource, username, password, applicantionId)
    adalContext.acquireTokenWithUsernamePassword(resource, username, password, applicantionId, adalCallback);
}
 
// //create DynamicsWebApi object
var dynamicsWebApi = new DynamicsWebApi({
    webApiUrl: 'https:/nulia.api.crm.dynamics.com/api/data/v9.0',
    onTokenRefresh: acquireToken,
    useEntityNames: true
});
 
const entityKey = '1156A6D0-20C9-E811-A94E-000D3A1F77E8'
dynamicsWebApi.retrieve(entityKey, 'contact', ['fullname']).then(function(record){
    console.log(record)
}).catch(function(error){
    //catch an error
    console.log("ERROR",error)
});```

Separate CrmWebApi version with Callbacks?

Question to a community:
Is CrmWebApi version with Callbacks needed?

If yes, would someone like it to be written using a plain XmlHttpRequest? Or a use of one of the following Ajax library preferred: jQuery or Superagent?

error on stringifying object type parameters in buildFunctionParameters(parameters)

The following code:

buildFunctionParameters({
    Target: {
        'accountid': 'e268122d-114b-e911-a98b-000d3aa00147',
        '@odata.type': 'Microsoft.Dynamics.CRM.account'
    }
})

returns:

(Target=@p1)?@p1=[object Object]

which is not correct.

the expected result would be:

(Target=@p1)?@p1={"accountid":"e268122d-114b-e911-a98b-000d3aa00147","@odata.type":"Microsoft.Dynamics.CRM.account"}

it seems like the code at buildFunctionParameters.js line 23:

urlQuery += "@p" + i + "=" + ((typeof value == "string") ? "'" + value + "'" : value;

could be changed to

urlQuery += "@p" + i + "=" + ((typeof value == "string") ? "'" + value + "'" : JSON.stringify(value));

to solved this issue.

"Xrm Context is not available" when used in Web Resource

When I tried to use the library in a web resource I got the "Xrm Context is not available" error every time. This was due to how the context is being grabbed from the dynamics-web-api.js file as it's looking for Xrm.Page directly instead of grabbing it from parent.Xrm.Page which is required in web resources. I fixed it with this minor change...

if (typeof Xrm != "undefined") {
  return Xrm.Page.context;
} else if (typeof parent.Xrm != "undefined") {
  return parent.Xrm.Page.context;
} else {
  throw new Error("Xrm Context is not available.");
}

I can submit a PR if that helps but wanted to see if this was already handled some other way.

Combination of multiple Prefer header values

Next release will have a significant change in the functionality which related to an ability to combine different Prefer values in the request header.

This feature has been noticed by me very late, so I will have to deprecate some existing DynamicsWebApi functionality. This functionality will still be available in the next release but can be removed in one of the future ones. Please follow the updates.

Thank you,
Aleksandr

Facing issue with Dynamics 365 online

Hello AleksandrRogov,

Thanks for the library. This is indeed very useful. But after adding "dynamics-web-api" as a web resource to entity form and when I try initializing class object var dynamicsWebApi = new DynamicsWebApi(); like this, I am getting error that DynamicsWebApi is not found. Am I missing anything over here ? Your feedback will be really helpful.

Thanks,
Mahesh G

Possible bug in function 'populateFormattedValues(object)'

Hi Alexandr.

I was retrieving multiple records of a custom entity with the version which uses Callbacks.

My code is like the following:
var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: '8.2' });

var filter = 'new_myfield eq <a_value>';

var select = ['new_field_1', 'new_field_2', 'new_field_3', 'new_field_2'];

dynamicsWebApi.retrieveMultiple('new_myentity', function (result) {
//My Logic
}, function (err) {
Xrm.Utility.alertDialog(err.message)
}, select, filter);

  • new_field_1, new_field_2, new_field_3, new_field_4 are all of type "Single Line of Text" (String)

I was retrieving a record which had new_field_2, new_field_3, new_field_4 empty (no value for the fields).

In run-time, entering the function 'populateFormattedValues(object)', the object has a null value for each of the corresponding keys:

function populateFormattedValues(object) {
var keys = Object.keys(object);

        for (var i = 0; i < keys.length; i++) {
            //Below object[keys[i]] is null for the empty fields on CRM
            if (object[keys[i]].constructor === Array) {
                for (var j = 0; j < object[keys[i]].length; j++) {
                    object[keys[i]][j] = populateFormattedValues(object[keys[i]][j]);
                }
            }
 ...

As a consequence, an exception is thrown because the code is trying to evaluate the property "constructor" of a null object.

I fixed the issue by adding a skipping step for the null elements, as shown below:

function populateFormattedValues(object) {
var keys = Object.keys(object);

        for (var i = 0; i < keys.length; i++) {
             if (object[keys[i]] == null)
                    continue;

            if (object[keys[i]].constructor === Array) {
                for (var j = 0; j < object[keys[i]].length; j++) {
                    object[keys[i]][j] = populateFormattedValues(object[keys[i]][j]);
                }
            }
 ...

After this fixing, the code is running properly, returning the correct object with parameters.

I would suggest you to make a deeper check and, in case, fix the code as I did or in a better way, and then publish the fixed version of the library.

Anyhow, congratulations for the very good job!

Using FetchXML with associated link-entity alias returns unexpected response values

Using the following fetchxml statement the results for the linked attributes (those referred to with an alias of 'td') are returned as "td_x002e_gr_amount". So instead of returning 'td.{attribute name} as expected this additional 'x002e' string is embedded. This string value appears to be consistent (so far) for any aliased attributes. Is there a reason for this? Is there a flag or option that I'm missing?

Regards,

Chuck

executeFetchXml fails

pagenumber replacer produces bad results for a fetch statement like this:
...
works if I add "." and "-" to the pattern:
fetchXml = fetchXml.replace(/^(<fetch[\w\d\s'"=.-]+)/, replacementString);
note that this is not a suggested fix but a quick workaround for my special use case.

401 Not authorized - successful token from adal-node

Having some trouble doing a basic query. I've hooked up adal-node and when I call acquireTokenWithClientCredentials I successfully get back an access token when I then pass to my dynamicsWebApi callback. Unfortunately, even with a valid token, I'm still getting an unauthorized error.

Am I supposed to pass the entire token response to the callback to just the accessToken itself (doesn't work either way, just trying to narrow down possibilities`

function acquireToken(dynamicsWebApiCallback) {                                                                                                                                                                                                           
  // a callback for adal-node (auth)                                                                                                                                                                                                                      
  function adalCallback(error, token) {                                                                                                                                                                                                                   
    if (!error) {                                                                                                                                                                                                                                         
      // call dynamicsWebApi callback only when token recieved                                                                                                                                                                                                  
       console.log(token) // this successfully shows the token response                                                                                                                                                                                                                        
      dynamicsWebApiCallback(token); // this always returns 401                                                                                                                                                                                                             
    } else {                                                                                                                                                                                                                                              
      console.log(`Whoops broke it: ${error}`);                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                                     
  }                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                          
  context.acquireTokenWithClientCredentials(                                                                                                                                                                                                              
    resource,                                                                                                                                                                                                                                             
    applicationId,                                                                                                                                                                                                                                        
    clientSecret,                                                                                                                                                                                                                                         
    adalCallback                                                                                                                                                                                                                                          
  )                                                                                                                                                                                                                                                       
}                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                          
const dynamicsWebApi = new DynamicsApi({                                                                                                                                                                                                                  
  webApiUrl: `${resource}api/data/v8.0`,                                                                                                                                                                                   
  onTokenRefresh: acquireToken,                                                                                                                                                                                                                                                                                                                                                                                                                           
}); 

dynamicsWebApi.retrieveMultiple("accounts").then(function (response) {
    console.log(response);
}).catch(function(error){
    console.log(error.message);
});

creating batch requests with upsert won't work

Hello,

i'm trying to setup a batch request with upserts. The problem is, that the promise won't be resolved on the upsert call.

A single upsert call works.

Example:

async function upsertData(token, dataSet) {
	function aquireToken(dynamicsWebApiCallback) {
		dynamicsWebApiCallback(token);
	}
	const dynamicsWebApi = new DynamicsWebApi({
		webApiUrl: 'endpoint',
		onTokenRefresh: aquireToken
	});

	dynamicsWebApi.startBatch();

	try {
		await Promise.all(dataSet.map(async(data) => {
			const response = await dynamicsWebApi.upsert(`key='${data.key}'`, 'transactions', data);
			return response;
		}));
	} catch (error) {
		console.log(error);
	}

	dynamicsWebApi.executeBatch();
}

Email addresses with '+' tags

Hello, loving the library - thanks very much for this!

I've come across a potential issue with the encoding of email addresses which contain a '+', e.g. [email protected]. Submitting a query with such an address in the filter argument of retrieveMultiple for example doesn't return any matches for me, even where a matching entity is known to exist in my Dynamics instance.

Making a change to line 213 in convertRequest in utilities/RequestConverter seems to fix the issue:

if (result.query) {
result.url += "?" + encodeURI(result.query);
}

at line 213 to

if (result.query) {
result.url += "?" + encodeURI(result.query).replace(/+/g, "%2B");
}

Thanks again

Donald

Getting error "Invalid URI: The hostname could not be parsed."

Hi All,

I am using the "dynamicsWebApi.update" method of the "dynamics-web-api" library to set a BPF stage of an entity as active in the CRM. But, I am getting below error:

"Invalid URI: The hostname could not be parsed.".

Please help me to solve it.

Thank you!!

Batch operation cache issue


var dynamicsWebApi = proEVENTS.WebResource.EventConfigurationHelper.Form.getApiObject();
                    dynamicsWebApi.startBatch();
                    // creates configuration and autonumbers in batch
                    dynamicsWebApi.create(configuration, "new_eventconfigurations");
                    dynamicsWebApi.create(autoNumbers1, "new_eventautonumbers");
                    dynamicsWebApi.create(autoNumbers2, "new_eventautonumbers");
                    dynamicsWebApi.create(autoNumbers3, "new_eventautonumbers");

                    dynamicsWebApi.executeBatch().then(function (responses) {
                        // read responses to fill the lookup
                        var configuration = responses[0];
                        var invitation = responses[1];
                        var registration = responses[2];
                        var event = responses[3];

                        //lookup needs odata
                        var configurationInvitationLookup = {
                            "[email protected]": "new_eventautonumbers(" + invitation + ")"
                        };

                        // lookup for registration
                        var configurationRegistrationLookup = {
                            "[email protected]": "new_eventautonumbers(" + registration + ")"
                        };

                        // lookup for event
                        var configurationEventLookup = {
                            "[email protected]": "new_eventautonumbers(" + event + ")"
                        };
                       
                        // start batch operation to fill the lookups
                        dynamicsWebApi.startBatch();
                        // lookup fill by configuration id, and autonumber id

                        dynamicsWebApi.update(configuration, "new_eventconfigurations", configurationInvitationLookup);
                        dynamicsWebApi.update(configuration, "new_eventconfigurations", configurationRegistrationLookup);
                        dynamicsWebApi.update(configuration, "new_eventconfigurations", configurationEventLookup);
                        
                        dynamicsWebApi.executeBatch().then(function (responsesUpdate) {

                            var serverURL = window.parent.Xrm.Page.context.getClientUrl();
                            $('#conf').attr('src', serverURL + "/main.aspx?etn=new_eventconfiguration&id={" + configuration + "}&newWindow=true&pagetype=entityrecord");
                        })

This code works in batch mode on a button click. Everything runs fine on the first click. if I click it again the update part does not work. If I refresh the page and try again it works but not for the second time. What can be the issue?

On-Premise Solution?

Okay, you have an example on how to authenticate against an online org and with an app in azure. How would you send a simple call to an on-premise org that needs authentication as well, with adfs/ifd ?

Upsert with AlternateKey causes "TypeError: Cannot read property '1' of null"error

The following sample works but causes an error.

When using a Guid instead everything works fine.
When setting "return=representation" Prefer Header the error also does not occur"
Tested with Dynamics webAPI 90 and 9.1

Code:

dynamicsWebApi.upsert("ad_key_str='0037'", "ad_akademischertitels", titel_update).then(function () {
console.log("Titel Upserted");
})
.catch(function (error) {
console.log(error.message);
});

Error:

TypeError: Cannot read property '1' of null
parseResponse.js:207
at parseResponse (d:[...]\node_modules\dynamics-web-api\lib\requests\helpers\parseResponse.js:207:102)
at IncomingMessage. (d:[...]\node_modules\dynamics-web-api\lib\requests\http.js:57:40)
at IncomingMessage.emit (events.js:187:15)
at endReadableNT (_stream_readable.js:1094:12)
at process._tickCallback (internal/process/next_tick.js:63:19)

List of npm Modules:

+-- [email protected]
| +-- @types/[email protected]
| +-- [email protected]
| | -- [email protected] | +-- [email protected] | +-- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | +-- [email protected] | | | | -- [email protected] deduped
| | | -- [email protected] deduped | | -- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | +-- [email protected] | | +-- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | +-- [email protected] deduped | | | -- [email protected] deduped
| | +-- [email protected]
| | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | -- [email protected] | | | | -- [email protected]
| | | -- [email protected] | | +-- [email protected] | | | +-- [email protected] | | | +-- [email protected] | | | | +-- [email protected] deduped | | | | +-- [email protected] | | | | +-- [email protected] | | | | -- [email protected]
| | | | +-- [email protected] deduped
| | | | +-- [email protected]
| | | | -- [email protected] deduped | | | -- [email protected]
| | | +-- [email protected]
| | | | -- [email protected] deduped | | | +-- [email protected] deduped | | | +-- [email protected] | | | | -- [email protected] deduped
| | | +-- [email protected]
| | | | -- [email protected] deduped | | | +-- [email protected] | | | | +-- [email protected] deduped | | | | -- [email protected] deduped
| | | +-- [email protected]
| | | | -- [email protected] deduped | | | +-- [email protected] | | | +-- [email protected] | | | -- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | -- [email protected] | | +-- [email protected] | | +-- [email protected] | | +-- [email protected] | | +-- [email protected] deduped | | +-- [email protected] | | | +-- [email protected] | | | -- [email protected] extraneous
| | +-- [email protected]
| | | -- [email protected] deduped | | -- [email protected] deduped
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| -- [email protected] -- [email protected]

Error: Xrm Context is not available.

Getting this Error while making

dynamicsWebApi.retrieveAllRequest(request).then(function (response) {
        console.log(request);
        var records = response.value;
        console.log(records);
        //do something else with a records array. Access a record: response.value[0].subject;
    })
    .catch(function (error){
        //catch an error
        console.log(error);
    });
Error: Xrm Context is not available. In most cases, it can be resolved by adding a reference to a ClientGlobalContext.js.aspx. Please refer to MSDN documentation for more details.
    at getXrmContext (/Users/beingsrv/Desktop/node-api/node_modules/dynamics-web-api/lib/utilities/Utility.js:34:11)
    at getClientUrl (/Users/beingsrv/Desktop/node-api/node_modules/dynamics-web-api/lib/utilities/Utility.js:38:19)
    at Object.initWebApiUrl (/Users/beingsrv/Desktop/node-api/node_modules/dynamics-web-api/lib/utilities/Utility.js:53:12)
    at DynamicsWebApi.setConfig (/Users/beingsrv/Desktop/node-api/node_modules/dynamics-web-api/lib/dynamics-web-api.js:139:49)
    at Object.exports.GetAllRecords (/Users/beingsrv/Desktop/node-api/app/httpRequest/httpGet.js:22:20)
    at /Users/beingsrv/Desktop/node-api/server.js:54:13
    at Layer.handle [as handle_request] (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/layer.js:95:5)
    at /Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/index.js:275:10)
    at /Users/beingsrv/Desktop/node-api/server.js:45:2
    at Layer.handle [as handle_request] (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/beingsrv/Desktop/node-api/node_modules/express/lib/router/index.js:317:13)

Unexpected error

Hello

I am trying to run the basic example but i keep getting an unexpected error:

{ Error: Unexpected Error
    at IncomingMessage.<anonymous> (/Users/araza/NodeScripts/CRM-scripts/node_modules/dynamics-web-api/lib/requests/http.js:82:33)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)
    at process._tickCallback (internal/process/next_tick.js:98:9) status: 401 }

Here is my code.. My client id and creds are correct because i tested by following the tutorial in this blog post:

http://alexanderdevelopment.net/post/2016/11/23/dynamics-365-and-node-js-integration-using-the-web-api/

Here is my code:

var DynamicsWebApi = require('dynamics-web-api');
var AuthenticationContext = require('adal-node').AuthenticationContext;

//the following settings should be taken from Azure for your application
//and stored in app settings file or in global variables

//OAuth Token Endpoint
var authorityUrl = 'https://login.windows.net<guid>/oauth2/token';
//CRM Organization URL
var resource = 'https://myorg.crm.dynamics.com';
//Dynamics 365 Client Id when registered in Azure
var clientId = '<my-client-id-here>';

var username = '<my-username>';
var password = '<my-password>';

var adalContext = new AuthenticationContext(authorityUrl);

//add a callback as a parameter for your function
function acquireToken(dynamicsWebApiCallback) {
  //a callback for adal-node
  function adalCallback(error, token) {
    if (!error) {
      //call DynamicsWebApi callback only when a token has been retrieved
      dynamicsWebApiCallback(token);
    } else {
      console.log('Token has not been retrieved. Error: ' + error.stack);
    }
  }

  //call a necessary function in adal-node object to get a token
  console.log('calling auth')
  adalContext.acquireTokenWithUsernamePassword(resource, username, password, clientId, adalCallback);
}


//create DynamicsWebApi object
var dynamicsWebApi = new DynamicsWebApi({
  webApiUrl: 'https://myorg.api.crm.dynamics.com/api/data/v8.2/',
  onTokenRefresh: acquireToken
});

//call any function
dynamicsWebApi.executeUnboundFunction("WhoAmI").then(function(response) {
  console.log('Hello Dynamics 365! My id is: ' + response.UserId);
}).catch(function(error) {
  console.log(error);
});

Please help. This package really looks promising and would love to use it on my project.

TypeScript: dynamics-web-api.d.ts is not a module

The following code is producing the following error.

Code:
import DynamicsWebApi from 'dynamics-web-api';

Error:
File 'c:/.../node_modules/dynamics-web-api/lib/dynamics-web-api.d.ts' is not a module.

Fetch picklist options

Is it possible to obtain the available picklist values for an attribute with this client library?

I'm currently using the following shell script to get the values I need but I'd like to automate this without using bash:

url="$(echo \
  "${base_url}" \
  "/EntityDefinitions(LogicalName=%27${entity}%27)" \
  "/Attributes(LogicalName=%27${attribute}%27)" \
  "/Microsoft.Dynamics.CRM.PicklistAttributeMetadata" \
  "?\$select=LogicalName" \
  "&\$expand=GlobalOptionSet(\$select=Options)" \
  | tr -d ' ')"

curl -f "${url}" \
  -H 'Accept: application/json' \
  -H 'OData-MaxVersion: 4.0' \
  -H 'OData-Version: 4.0' \
  -H 'Content-Type: application/json; charset=utf-8' \
  -H "Authorization: Bearer ${token}" \
  | jq '
    .GlobalOptionSet.Options[] |
    {
      value: .Value,
      label: .Label.LocalizedLabels[0].Label
    }'

The main issue with my bash script is that I have to manually generate and copy the auth token. Perhaps a more generic API should be exposed so that edge cases like this can be implemented by dropping down to the OData protocol like:

const response = await dynamicsWebApi.odataGet(path);

or you could expose the token like:

const token = await dynamicsWebApi.getToken();

Collection-valued navigation

Hi guys,

Just wondering if this library handles Collection-valued navigation?

I've noticed if I try to use the expand property of the request object it just returns a @odata.nextLink. Naturally I'd want to avoid having to run a request to grab this data.

Is there anyway to handle that from the library?

Cheers,
Mark.

How to abort chained .then() functions

What technique would I use to abort a chain of .then() statements?

I can use the javascript try/catch/throw, but was looking for something intrinsic to the Promise spec. Are there other options available?

Chuck

trouble with retreiveMultiple filter

I am trying to get all accounts where transactioncurrencyid has guid 784c6a92-....
I am using retreiveMultiple with filter statement
transactioncurrencyid eq 784c6a92-...
This gives me an exception:

"message": "A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.transactioncurrency' and 'Edm.Guid' for operator kind 'Equal'.",
"type": "Microsoft.OData.Core.ODataException",

If instead I use the filter statement
_transactioncurrencyid_value eq 784c6a92-...
all is fine and I get my records.

I think this should be done automatically by the library.

Synchronous request

Hello,

How can I use your library if I want to make synchronous request ? Because I think all requests are asynchrounous.

Thanks,
Sebastien

dynamics-web-api.d.ts has optional parameter followed by mandatory

Hi,

I'm just trying to pack into Docker container an app that relies on dynamics-web-api package. When I'm running build inside a container, I'm getting the following:

ERROR in /app/node_modules/dynamics-web-api/types/dynamics-web-api.d.ts
ERROR in /app/node_modules/dynamics-web-api/types/dynamics-web-api.d.ts(294,37):
TS1016: A required parameter cannot follow an optional parameter.

Indeed dynamics-web-api.d.ts at line 294 has this line:

executeBoundAction(id?: string, collection: string, actionName: string, requestObject?: Object, impersonateUserId?: string): Promise<any>;
                   ^^^

I'm guessing this is why the app isn't able to build. So, is it a mistake or am I missing something?

FetchXml Advanced Request

Any plans to implement an "advanced" version of the FetchXml Requests? I'm passing a token explicitly to my calls using the Advanced Request object elsewhere, but I noticed that Fetch currently doesn't support that.

I "hacked" my way into passing an additional parameter for my token into the basic FetchXml request, and one downstream thing I noticed is that when my FetchXml request is split into a "batch", the Authorization header is removed. I see you have supporting batch requests on the road map, so I imagine this fix will be relevant there as well. This was relatively easy to fix by adding an additional line after line 131 in sendRequest.js:

    for (var key in additionalHeaders) {
        batchBody.push(key + ': ' + additionalHeaders[key]);
        **if (key != 'Authorization') delete additionalHeaders[key];**
    }

Thanks again for all your work on the library!

FetchXml aliased results are different between CRM 2016 (on-premise) and Dyn 365

Just installed a solution that is works fine on an on-premise CRM 8.2 instance, onto an online Dynamics 9.x and hit a problem with all the FetchXml requests that contained an aliased link-entity. Seems the resulting javascript/JSON result is returning the attribute named differently.

I know we'd just worked through another issue (#25) on this same topic, but this is a slightly different issue. I've also tried your earlier version (prior to 1.4.2) but that didn't seem to have any effect.

Via debugger, the Dyn365 instance shows it's encasing all aliased variables with quotes. So what is referenced as simply ad.category in 8.2 is now show as "ad.category" in 9.x and I've so far been unable to figure out how to successfully access that variable w/o causing an undefined error.

I'm attaching screen prints that show the differences. Any input would be appreciated.

Dyn9x_variables.png - Shows quoted variables "ad.cac_categorytype" & "ad.cac_subcategory"

Dyn8.2_variables.png - Shows debug from a Dyn 8.2 instance where the variables are not quoted. Also shows the "ad" class with a _dwaType: "alias" entry -- which is not even in the 9x returned obj

Sample_FetchXml.txt - the FetchXml statement being used (just in case).

Regards,

Chuck

dyn8 2_variables
ect.

sampleFetchXml.txt

dyn9x_variables

What should the authorityUrl be when connecting to an On-Premise installation?

Submitting this as an issue as requested, thank you.

What should the authorityUrl variable be when connecting to an on-premise installation of the Microsoft Dynamics CRM? This is regarding this part - https://www.npmjs.com/package/dynamics-web-api#dynamicswebapi-for-nodejs . If I use https://login.microsoftonline.com/< client id >/oauth2/token then I get the below error, the angle brackets were added by me.

Token has not been retrieved. Error: Error: Get Token request returned http error: 400 and server response: {"error":"invalid_request","error_description":"AADSTS90002: Tenant '< client id >' not found. This may happen if there are no active subscriptions for the tenant. Check with your subscription administrator. ...

How to run code sample for Node version

Hi, this is my first time interacting any of this. So please consider me as a total noob.

I'm building a website for a client that requires the use of Dynamics CRM as the backend for the membership system. And they refer me to this library.

I'm trying out the adal-node code example in your README. But it gives me a timeout error.

I saved the example code to a start.js.
Udated the config with the ones my client gave me:

var authorityUrl = 'https://login.microsoftonline.com/<tenant>/oauth2/token';
var resource = '<resource>'; // https://xxx.dynamics.com/
var clientId = '<client_id>';
var username = '<username>';
var password = '<password>';

Run it in the Terminal.

node start.js

Then after waiting quite a while, i got this error message:

Error: connect ETIMEDOUT xxx.xx.xxx.xx:443

So did i miss anything?

includeAnnotations: "Microsoft.Dynamics.CRM.lookuplogicalname" ignored

I am calling dwa.rerieveRequest with property
includeAnnotations: "Microsoft.Dynamics.CRM.lookuplogicalname"
For some unknown reason this returns a result header with preference-applied: "odata.include-annotations="OData.Community.Display.V1.FormattedValue"" and indeed, the returned data includes formatted values but no logicalName info. Is this expected behavior with dynamics 365 V9 Online? I realize that this is probably not an issue with your excellent library, but you might be able to give me some clues how to solve this nonetheless.

Associate error

thank you for your quick help last time, hopefully this one isn't as bad of a user error.
Code
dynamics .associate( 'contacts', record.contactid, 'parentcustomerid', 'accounts', parentcustomerid ) .then((data) => { console.log('link worked', data) context.res = { body: record } })

Error
link failed { code: '0x8006088a', message: 'The URI segment \'$ref\' is invalid after the segment \'parentcustomerid\'.', innererror: { message: 'The URI segment \'$ref\' is invalid after the segment \'parentcustomerid\'.', type: 'Microsoft.OData.ODataException', stacktrace: ' at System.Web.OData.Routing.DefaultODataPathHandler.Parse(String serviceRoot, String odataPath, IServiceProvider requestContainer, Boolean template)\r\n at System.Web.OData.Routing.DefaultODataPathHandler.Parse(String serviceRoot, String odataPath, IServiceProvider requestContainer)\r\n at Microsoft.Crm.Extensibility.ODataV4.Routing.CrmODataPathHandler.Parse(String serviceRoot, String odataPath, IServiceProvider requestContainer)' },

Recursive Call for Retrieve Multiple All

Couldn't find any way to contact you other than to log an issue. Really appreciate all of the work you've put into this library. I wanted to pass along a recursive method wrapper for retrieveMultipleAll that I wrote using your retrieveMultiple call as I see you have this on the roadmap. Hope it helps.

        function selectContact() {
            retrieveMultipleAll("contacts", ["fullname", "telephone1", "emailaddress1"],
                "statecode eq 0").then(function (records) {
                    console.log(records.length);
                    console.log(records);
                }).catch(function (error) {
                    console.log(error);
                });

        }

        function retrieveMultipleAll(entityname, columns, filter, entities, oDataNextLink) {
            var dynamicsWebApi = new DynamicsWebApi({
                webApiVersion: "8.2",
                maxPageSize: 5,
                includeAnnotations: "OData.Community.Display.V1.FormattedValue"
            });
            entities = entities || [];
            
            var promise = $q.defer();

            if (oDataNextLink) {
                dynamicsWebApi.retrieveMultiple(entityname, columns, filter, oDataNextLink).then(function (records) {
                    entities = entities.concat(records.value);

                    if (records.oDataNextLink) {
                        promise.resolve(retrieveMultipleAll(entityname, columns, filter, entities, records.oDataNextLink));
                    }
                    else promise.resolve(entities);
                }).catch(function (error) {
                    console.log(error);
                    promise.error(error);
                });
            } else {
                dynamicsWebApi.retrieveMultiple(entityname, columns, filter).then(function (records) {

                    entities = entities.concat(records.value);
                    if (records.oDataNextLink) {
                        promise.resolve(retrieveMultipleAll(entityname, columns, filter, entities, records.oDataNextLink));
                    }
                    else promise.resolve(entities);
                }).catch(function (error) {
                    console.log(error);
                    promise.error(error);
                });
            }
            
            return promise.promise;
        }

Create record with lookup field

Is there a way to create a record with a lookup field? Something like:

//initialize a CRM entity record object
var lead = {
_regardingobjectid_value: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
subject: "Test WebAPI",
firstname: "Test",
lastname: "WebAPI",
jobtitle: "Title"
};
//call dynamicsWebApi.create function
dynamicsWebApi.create(lead, "leads").then(function (id) {
//do something with id here
}).catch(function (error) {
//catch error here
})

Issue with statecode

Is there an issue with updating statecode on activities? I am trying to set statecode: 1, but I cannot update the field. I am not getting an error, but the activity doesn't change in Dynamics.

Authentication Issue with Oatuh API

Hi,

Getting an 401 when trying to access the API:

message:"Get Token request returned http error: 401 and server response: {"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: \r\nCorrelation ID: \r\nTimestamp: 2019-07-18 14:46:12Z","error_codes":[7000218],"timestamp":"2019-07-18 14:46:12Z","trace_id":"","correlation_id":""}"

Would appreciate your help.

Edan.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.