aleksandrrogov / dynamicswebapi Goto Github PK
View Code? Open in Web Editor NEWDynamicsWebApi is a Microsoft Dataverse Web API helper library for JavaScript & TypeScript
License: MIT License
DynamicsWebApi is a Microsoft Dataverse Web API helper library for JavaScript & TypeScript
License: MIT License
Is it possible to use query functions such as LastXMonths (https://docs.microsoft.com/en-us/dynamics365/customer-engagement/web-api/lastxmonths) as filter statements? If I set the filter statement to 'Microsoft.Dynamics.CRM.LastXMonths(PropertyName=@p1,PropertyValue=@p2)&@p1='name'&p2=42'
I receive the server response
"Syntax error: character '&' is not valid at position 70 in 'Microsoft.Dynamics.CRM.LastXMonths(PropertyName=@p1,PropertyValue=@p2)&@p1='name'&p2=42'."
Is it possible to use the change tracking functionality of Dynamics as described here? I can't find anything documented to suggest if/how to do this.
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 :-)
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
});
});
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.
}
When using an aggregate function within the fetchxml statement it fails because paging information are added.
Thanks for the library. It really helpful.
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.
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!
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?
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)
});```
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?
The following code:
buildFunctionParameters({
Target: {
'accountid': 'e268122d-114b-e911-a98b-000d3aa00147',
'@odata.type': 'Microsoft.Dynamics.CRM.account'
}
})
returns:
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.
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.
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
Is there a way to make requests using alternate keys?
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
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);
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 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
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.
I am using adal.js from https://github.com/AzureAD/azure-activedirectory-library-for-js/tree/dev/dist for a client app (not a node app) that requires authorization. In this adal version the returned token is the token string itself. The token property accessToken is undefined.
Workaround: add (token.accessToken || token) to the auth header instead.
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);
});
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();
}
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
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!!
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?
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 ?
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]
When I make concurrent requests on DynamicsWebApi.fetch(...)
, sometimes the response is an array instead of an object. The first entry looks exactly like the response I expect. Is this behaviour a bug or a feature?
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)
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:
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.
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.
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();
As per described here; https://msdn.microsoft.com/en-ca/library/gg327838.aspx
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.
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
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.
Hello,
How can I use your library if I want to make synchronous request ? Because I think all requests are asynchrounous.
Thanks,
Sebastien
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?
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!
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
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. ...
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?
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.
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)' },
I'm getting the error: Unexpected token ...
This is because of this line:
DynamicsWebApi/lib/requests/http.js
Line 64 in 978500d
This was introduced in this commit: bcf74c0
I always thought kind of extending of a dictionary is only available using TypeScript of any really modern JavaScript.
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;
}
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
})
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.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.