Hsl.WebApi.Client
To create an instance of the Service class, call Hsl.WebApi.getClient(apiVersion)
. You can only specify API versions that are supported by the platform version you're connecting to.
Data Modification
Values to Pass for Create/Update/Upsert Requests
When passing data to the webapi endpoint, the value of fields should match what's shown in the sample below.
function dataValuesSample() {
const client = Hsl.WebApi.getClient('9.1');
// This sample is for update but you'll want to do the same thing for create/upsert.
client.update({ entityType: 'account', id: 'adaaa79b-4054-4035-8cf4-b627571e1397' }, {
// String fields
hsl_string: 'My Test Account',
hsl_memo: 'My multiple\r\nlines of text',
// Numeric types - just a regular javascript number.
hsl_wholenumber: 5,
hsl_floatingpoint: 5.6,
hsl_decimal: 5.6,
hsl_money: 5.63,
// Picklist - Numeric value for the field
hsl_picklist: 296010001,
// And an array for multiselect
hsl_multiselect: [296010001, 296010005],
hsl_multiselect2: [],
statecode: 0,
statuscode: 4,
hsl_boolean: true,
// Date Only date time behavior
hsl_dateonly: '2020-04-20',
hsl_dateonly2: Hsl.WebApi.encodeDateOnly(new Date()),
// User Local date time behavior
hsl_datetimeuserlocal: new Date(),
hsl_datetimeuserlocal2: '2020-08-03T12:50:23Z',
// Time zone independent date time behavior
hsl_datetimetzi: Hsl.WebApi.encodeTziDateTime(new Date()),
hsl_datetimetzi2: '2020-08-03T12:50:23-00:00',
// Image - base64 representation of the image.
// NOTE: Image data transfers from the web service endpoints are limited to a maximum
// of 16 MB data in a single service call.
// See https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/image-attributes
// For larger images, use:
// Hsl.WebApi.getClient('9.0').uploadFile(accountReference, 'hsl_image', fileBlobContent);
// This will avoid the overhead of base64 encoding and automatically chunks the file
// into 4MB segments.
hsl_image: 'iVBORw0KGgoAAAANSUhEUgAAAJAAAACQCAIAAABoJHXvAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFiSURBVHhe7dFBDcAwAMSwW/lz7j4lEcn+BEC+7Y6O80qEYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGxRgWY1iMYTGGpWw/pEcCH70HnB0AAAAASUVORK5CYII=',
// For file attributes, use
// Hsl.WebApi.getClient('9.0').uploadFile(accountReference, 'hsl_file', fileBlobContent);
// Set lookup fields using bindReference. Be sure you're using the correct casing for
// the navigation property. Generally, this tends to be the cased schema name for
// custom fields and the all lowercase name for out of the box fields.
// Check the webapi metadata to verify a particular property.
hsl_ContactLookupId: Hsl.WebApi.bindReference('contact', 'A83C54A1-59CC-4AB2-AF37-A0550A07A525'),
primarycontactid: Hsl.WebApi.bindReference('contact', 'A83C54A1-59CC-4AB2-AF37-A0550A07A525'),
ownerid: Hsl.WebApi.bindReference('systemuser', 'e2949df3-65f8-49c4-8b15-005c22ddffcb'),
});
client.create('task', {
// regardingobjectid is set using the
// regardingobjectid_ENTITYSCHEMANAME navigation property.
regardingobjectid_account: Hsl.WebApi.bindReference('account', 'b61d7b93-e9c5-41ce-9487-76218c81442c'),
});
client.create('annotation', {
// objectid is set using the objectid_ENTITYSCHEMANAME navigation property.
objectid_account: Hsl.WebApi.bindReference('account', 'b61d7b93-e9c5-41ce-9487-76218c81442c'),
});
}
create
client.create(entityName: string, record: any, opts?: RequestSettings): Promise<string>;
Create a new record with the given values. The method returns a promise of the primary id of the created record.
function webApiCreateSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.create('account', {
// Specify attribute values for the created account.
name: 'My Test Account',
// You can specify an object on navigation properties to do a compound create creating the account/contact in
// the same request in this example. The navigation properties can either be 1:N or N:1
primarycontactid: {
firstname: 'Contact',
lastname: 'Test',
},
opportunity_customer_contacts: [
{
name: 'My Test Opportunity A'
},
{
name: 'My Test Opportunity B'
},
],
// Set lookup fields using bindReference. Be sure you're using the correct casing for the navigation property.
hsl_ContactLookupId: Hsl.WebApi.bindReference('contact', 'A83C54A1-59CC-4AB2-AF37-A0550A07A525'),
});
}
createAndGet
client.createAndGet(entityName: string, record: any, select: null | string[], opts?: GetRequestSettings): Promise<WebApiRecord>;
Create a new record with the given values. The method returns a promise of the record with the specified columns.
function createAndGetSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.createAndGet('account', {
name: 'abc',
}, ['hsl_autonumberfield', 'createdon']).then(record => {
console.log(record.hsl_autonumberfield);
console.log(record.createdon);
return record.hsl_autonumberfield;
});
}
update
client.update(recordReference: WebApiReference, record: any, _opts?: UpdateRequestSettings): Promise<void>
Updates the specified record with the specified values.
function updateSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.update({ entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' }, {
name: 'Updated Name',
});
}
function updateOptimisticConcurrency() {
// See https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/optimistic-concurrency
// for additional details.
const client = Hsl.WebApi.getClient('9.1');
const accountRef = { entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' };
return client.retrieve(accountRef, ['name']).then(record => {
// Only updates the account if it has NOT been updated since it was retrieved.
// Will error if the record was modified.
return client.update(accountRef, {
name: record.name + ' (Test)',
}, {
etag: record['@odata.etag'],
});
});
}
updateAndGet
client.updateAndGet(recordReference: WebApiReference, record: any, select: null | string[], _opts?: UpdateRequestSettings & GetRequestSettings): Promise<WebApiRecord>
Updates the specified record with the specified values. Returns a promise of the record with the specified columns.
function updateAndGetSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.updateAndGet({ entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' }, {
name: 'Updated Name',
}, ['hsl_fieldsetbyplugin']).then(record => {
console.log(record.hsl_fieldsetbyplugin);
});
}
upsert
client.upsert(recordReference: WebApiReference, record: any, _opts?: UpdateRequestSettings): Promise<void>
Upserts a record by alternate key.
function upsertSample() {
const client = Hsl.WebApi.getClient('9.1');
// Assumes accountnumber has been set up as an alternate key for the account entity.
return client.upsert({ entityType: 'account', accountnumber: '123' }, {
name: '123 Company',
telephone1: '814-555-1234',
});
}
upsertAndGet
client.upsertAndGet(recordReference: WebApiReference, record: any, select: null | string[], _opts?: UpdateRequestSettings & GetRequestSettings): Promise<WebApiRecord>
Upserts a record by alternate key and retrieves it.
delete
client.delete(recordReference: WebApiReference, opts?: DeleteRequestSettings): Promise<void>;
Deletes the specified record.
interface DeleteRequestSettings extends RequestSettings {
/** When specified, if the etag of the record on the server doesn't match the etag passed by the client,
* the server will abort the request. This can be used to ensure the request will not go through if
* another use made changes to the record. */
etag?: string;
}
See RequestSettings
function deleteSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.delete({ entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' });
}
function deleteOptimisticConcurrencySample() {
// See https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/optimistic-concurrency
// for additional details.
const client = Hsl.WebApi.getClient('9.1');
const accountRef = { entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' };
return client.retrieve(accountRef, ['statecode']).then(record => {
if (record.statecode === 0)
return;
// Only deletes the account if it has NOT been updated since it was retrieved.
// Will error if the record was modified.
return client.delete(accountRef, {
etag: record['@odata.etag'],
});
});
}
associate
client.associate(relationshipName: string, target: WebApiReference, associateWith: PrimaryIdReference, opts?: RequestSettings): Promise<void>
Associates two records according to a relationship.
⚠️ If you're associating a N:N relationship, parameter order matters. The first parameter should be the entity you created the N:N relationship from. If you used the default generated relationship name, it will be the first entity in the generated name. For example, if the N:N relationship name is hsl_account_contact, you should call .associate('hsl_account_contact', accountRef, contactRef). Using .associate('hsl_account_contact', contactRef, accountRef) would throw an error.
function associateManyToManySample() {
const client = Hsl.WebApi.getClient('9.0');
return client.associate('hsl_manytomanyrelationshipname', {
entityType: 'account',
id: '{DD625F5C-E49D-414E-9D0E-69B98440506F}',
}, {
entityType: 'new_otherentity',
id: '{AD640C47-D597-4DE8-B97F-7D781AF43061}',
});
}
disassociate
client.disassociate(relationshipName: string, target: WebApiReference, disassociateWith?: PrimaryIdReference | null | undefined, _opts?: RequestSettings): Promise<void>
Either removes a N:N relationship between two records or sets a lookup field to null.
relationshipName
The relationship (Navigation property) name.target
The record to disassociate.disassociateWith
The record to disassociate with on the other side of the relationship. Use null/undefined if setting a lookup field to null.opts
request options
⚠️ If you're disassociating a N:N relationship, parameter order matters. The first parameter should be the entity you created the N:N relationship from. If you used the default generated relationship name, it will be the first entity in the generated name. For example, if the N:N relationship name is hsl_account_contact, you should call .associate('hsl_account_contact', accountRef, contactRef). Using .associate('hsl_account_contact', contactRef, accountRef) would throw an error.
function disassociateManyToManySample() {
const client = Hsl.WebApi.getClient('9.0');
return client.disassociate('hsl_manytomanyrelationshipname', {
entityType: 'account',
id: '{DD625F5C-E49D-414E-9D0E-69B98440506F}',
}, {
entityType: 'new_otherentity',
id: '{AD640C47-D597-4DE8-B97F-7D781AF43061}',
});
}
function setLookupFieldToNull() {
const client = Hsl.WebApi.getClient('9.0');
// Set the preferredsystemuserid of account to null.
// The first parameter should be the navigation property name.
return client.disassociate('preferredsystemuserid', {
id: '{840957BE-0AEA-49B7-8CE4-A84CF3F7B9FF}',
entityType: 'account',
});
}
uploadFile
See:
- https://developer.mozilla.org/en-US/docs/Web/API/Blob
- https://developer.mozilla.org/en-US/docs/Web/API/File
client.uploadFile(recordReference: WebApiReference, attributeName: string, file: FileInfo): Promise
Updates the data for either a file or image attribute.
function uploadFileSample() {
const client = Hsl.WebApi.getClient('9.1');
const file = new Blob(['Hello World'], {
type: 'text/plain',
});
file.name = 'MyTestFile.txt';
return client.uploadFile({ entityType: 'hsl_quote', id: '{97767165-7EB8-4885-BECD-DD5A7D4A8EB1}' }, 'hsl_textfile', file);
}
function uploadFileUsingHtmlInputSample() {
const client = Hsl.WebApi.getClient('9.1');
// Add a file input to the document.
const input = document.createElement('input');
input.type = 'file';
document.body.appendChild(input);
input.addEventListener('change', ev => {
if (!input.files || input.files.length === 0)
return;
const file = input.files.item(0);
if (!file)
return;
input.value = ''; // Clear the input to let the user select a different file
const dlg = Hsl.Dialog.showSpinner();
client.uploadFile({ entityType: 'hsl_quote', id: '{97767165-7EB8-4885-BECD-DD5A7D4A8EB1}' }, 'hsl_pdfsendtocustomer', file)
.catch(Hsl.Dialog.showError)
.then(dlg.close);
});
}
Data Retrieval
retrieve
client.retrieve(recordReference: WebApiReference, columns: string[], expand?: ExpandDefinition, _opts?: RetrieveRequestSettings): Promise<WebApiRecord>
Retrieves a single record by primary id or alternate key. The "columns" parameter specifies the list of attributes to query out. If columns is an empty array, all columns are retrieved. It is recommended to always specify specified columns instead of retrieving all for better performance.
export interface RetrieveRequestSettings extends GetRequestSettings {
/** When specified, if the etag of the record on the server doesn't match the etag passed by the client,
* the server will return that the record hasn't been modified instead of passing down the data again.
* Then, when the Promise resolves, it will resolve to null instead of the values of the record. */
etag?: string;
}
function basicRetrieveSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.retrieve({ entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' }, ['name']);
}
function retrieveByAlternateId() {
const client = Hsl.WebApi.getClient('9.0');
return client.retrieve({ entityType: 'account', accountnumber: '301925' }, ['industrycode'], undefined, { formattedValues: true }).then(resp => {
return resp.getFormattedValue('industrycode');
});
}
function retrieveExpandSample() {
const client = Hsl.WebApi.getClient('9.0');
return client.retrieve({ entityType: 'account', id: '7fb47cc4-fa9a-4048-a455-5e7a58d0a319' }, ['name', 'industrycode'], {
// Expand 1-N relationship.
contact_customer_accounts: ['fullname', 'preferredcontactmethodcode'],
// Nested expand - get the primary contact's account info
primarycontactid: {
select: ['fullname'],
expand: {
parentcustomerid_account: ['name', 'accountnumber'],
},
},
}, { formattedValues: true }).then(record => {
const primaryContact = record.primarycontactid;
if (primaryContact) {
const primaryContextParent = primaryContact.parentcustomerid_account;
if (primaryContextParent) {
console.log(primaryContextParent.accountnumber);
}
}
const childContactArray = record.contact_customer_accounts;
console.log(`This account has ${childContactArray.length} contacts.`);
const nameList = childContactArray.map((x) => x.fullname);
console.log(`Their names are ${nameList.join(' & ')}`);
}).catch(Hsl.Dialog.showError);
}
function retrieveWithEtag() {
const client = Hsl.WebApi.getClient('9.0');
const accountRef = { entityType: 'account', id: 'f99a0e3e-90d5-ea11-a813-002248049ffb' };
const selectCols = ['name'];
let record = null;
setInterval(() => {
// Retrieve - deltaRecord will be null if the record is unchanged.
client.retrieve(accountRef, selectCols, undefined, {
etag: record && record['@odata.etag'],
}).then(deltaRecord => {
let action;
if (!record)
action = 'loaded';
else
action = deltaRecord ? 'changed' : 'unchanged';
console.log(`Record was ${action}`);
if (deltaRecord)
record = deltaRecord;
}).catch(error => {
console.error(`Last retrieve failed with error`);
console.error(error);
});
}, 5000);
}
fetch
client.fetch(fetchXml: string | XMLDocument, _opts?: GetRequestSettings): Promise<WebApiResultSet>
Executes the specified fetch xml query. This will only get the first page of results. See fetchAll for a function that will automatically page beyond the first 5000 records.
function fetchSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.fetchAll(`<fetch>
<entity name="task">
<attribute name="subject" />
<filter>
<condition attribute="regardingobjectid" operator="eq" value="006ac1c2-b206-45f9-a306-3cbd21417db5" />
</filter>
</entity>
</fetch>`, {
formattedValues: true,
timeout: 1000 * 60,
});
}
fetchAll
client.fetchAll(fetchXml: string | XMLDocument, opts?: QueryAllRequestSettings): Promise<WebApiRecord[]>;
function fetchAllSample() {
// Loads all tasks for the specified regarding object.
const client = Hsl.WebApi.getClient('9.1');
return client.fetchAll(`<fetch>
<entity name="task">
<attribute name="subject" />
<filter>
<condition attribute="regardingobjectid" operator="eq" value="006ac1c2-b206-45f9-a306-3cbd21417db5" />
</filter>
</entity>
</fetch>`, {
formattedValues: true,
timeout: 1000 * 60,
});
}
Executes the specified fetch xml query. This function will automatically page through results beyond the 5000 limit similar to queryAll.
query
client.query(entityName: string, query: QueryDefinition, opts?: GetRequestSettings): Promise<WebApiResultSet>;
Executes a WebAPI query. This will only get the first page of results. See queryAll for a function that will automatically page beyond the first 5000 records.
// https://msdn.microsoft.com/en-us/library/mt608066.aspx
// Data types:
// String: use Hsl.encodeWebApiString
// Date:
function queryContactsThatPreferPhoneContactInCity() {
const client = Hsl.WebApi.getClient('9.0');
// See https://msdn.microsoft.com/en-us/library/gg334767.aspx#bkmk_applyqueryOptions
// Query contacts where the preferred contact method is 'Phone' and the telephone1 field is not null.
client.query('contact', {
filter: [
'preferredcontactmethodcode eq 3',
'telephone1 ne null'
],
select: ['fullname', 'telephone1'],
orderby: 'fullname',
}).then(resp => {
if (resp.value.length > 0) {
const msg = resp.value.map(c => c.getValue('fullname') + ': ' + c.getValue('telephone1')).join('\r\n');
Hsl.Dialog.alert(msg);
}
else {
Hsl.Dialog.alert('No results found');
}
});
}
function inFilterQuery() {
const client = Hsl.WebApi.getClient('9.0');
// https://msdn.microsoft.com/en-us/library/mt607541.aspx
// Because of how In is defined, the values are always strings regardless of the property type.
return client.query('account', {
select: ['name', 'accountnumber', 'industrycode'],
filter: Hsl.WebApi.encodeFunction('.In', {
PropertyName: 'industrycode',
PropertyValues: ['5', '8'],
}),
});
}
function underFilterQuery() {
const client = Hsl.WebApi.getClient('9.0');
// Queries all child accounts under the given accountid
return client.query('account', {
filter: Hsl.WebApi.encodeFunction('.Under', {
PropertyName: 'accountid',
PropertyValue: '{FAED9189-F9C8-461F-BA91-89BC1B79AD2D}',
}),
select: ['accountid']
});
}
function findJohnsWithLastName(lastname) {
const client = Hsl.WebApi.getClient('9.0');
client.query('contact', {
// Put single quotes around the name to encode it.
// For dynamic content, encode it with encodeWebApiString.
// This will allow us to handle last names with apostrophes in them.
filter: "firstname eq 'John' and lastname eq " + Hsl.WebApi.encodeString(lastname),
select: ['fullname', 'telephone1']
}).then(resp => {
if (resp.value.length > 0) {
const msg = resp.value.map(c => c.fullname + ': ' + c.telephone1).join('\r\n');
Hsl.Dialog.alert(msg);
}
else {
Hsl.Dialog.alert('No results found');
}
}).catch(Hsl.Dialog.showError);
}
function queryBasedOnLookupValue(accountId) {
const client = Hsl.WebApi.getClient('9.0');
// Find opportunties for the current record based on the transaction currency and the account.
return client.query('opportunity', {
select: ['name', 'estimatedvalue'],
filter: [
Hsl.WebApi.getValuePropertyName('transactioncurrencyid') + ' eq EF8379E0-B9A8-E511-80BD-0800278A357E',
'_parentaccountid_value eq ' + accountId,
],
});
}
function builtInFunctions() {
const client = Hsl.WebApi.getClient('9.0');
client.query('account', {
select: 'name',
filter: `contains(name, 'test') and industrycode eq 1`
}).then(resp => {
const msg = resp.value.map(a => a.name).join(', ');
Hsl.Dialog.alert(msg);
}).catch(Hsl.Dialog.showError);
}
function specialFunctions(context) {
const client = Hsl.WebApi.getClient('9.0');
// https://msdn.microsoft.com/en-us/library/mt607843.aspx lists available functions
// Query for accounts that have been created in the last 7 days OR accounts that are descendent accounts
// Prepending with '.' is a shortcut for "Microsoft.Dynamics.CRM.".
// Find records created in the last 7 days.
const createdOnFilter = Hsl.WebApi.encodeFunction('.LastXDays', {
PropertyName: 'createdon',
PropertyValue: 7,
});
// Find descendent accounts
const descendentAccountFilter = Hsl.WebApi.encodeFunction('.Under', {
PropertyName: 'accountid',
PropertyValue: '{7C586287-8B70-4C71-8834-C10B0208C673}',
});
return client.query('account', {
select: 'name',
filter: createdOnFilter + ' or ' + descendentAccountFilter,
}).then(resp => {
const msg = resp.value.map(a => a.name).join(', ');
Hsl.Dialog.alert(msg);
return resp.value;
});
}
function pagingExample() {
const client = Hsl.WebApi.getClient('9.0');
// We want to make sure that the options we pass for each page is the same so store it as a variable.
const queryOpts = {
formattedValues: true,
pageSize: 1,
};
// Page through results.
client.query('account', {
orderby: 'name',
select: ['name', 'industrycode'],
}, queryOpts).then(_resp => {
let curResults = _resp;
Hsl.Dialog.open(buildDialog());
function buildDialog() {
const buttons = {
Next(dlg) {
const spn = Hsl.Dialog.showSpinner();
client.get(curResults['@odata.nextLink'], queryOpts).then(_resp => {
curResults = _resp;
dlg.update(buildDialog());
}).catch(Hsl.Dialog.showError).then(() => spn.close());
},
Close(dlg) {
dlg.close();
},
};
if (!curResults['@odata.nextLink']) {
// We are on the last page don't show the "Next" button.
delete buttons['Next'];
}
return {
text: curResults.value[0].name + ': ' + curResults.value[0].getFormattedValue('industrycode'),
buttons,
};
}
}).catch(Hsl.Dialog.showError);
}
function webApifetchExample() {
const client = Hsl.WebApi.getClient('9.0');
// Find accounts without a contact
client.fetch(`<fetch version="1.0" output-format="xml-platform" mapping="logical">
<entity name="account">
<attribute name="name" />
<order attribute="name" descending="false" />
<link-entity name="contact" from="parentcustomerid" to="accountid" alias="aa" link-type="outer">
<filter type="and">
<condition attribute="contactid" operator="null" />
</filter>
</link-entity>
</entity>
</fetch>`).then(resp => {
Hsl.Dialog.open({
contentHtml: '<div id="panel"></div>',
onOpen(dlg) {
const pnl = dlg.find('#panel');
if (resp.value.length === 0)
pnl.text('No results found');
resp.value.forEach(account => {
dlg.$('<a></a>')
.text(account.name)
.css({
display: 'block',
width: '15em',
color: 'blue',
'text-decoration': 'underline',
})
.click(() => {
Hsl.openEntityForm('account', account.accountid, undefined, true);
return false;
})
.appendTo(pnl);
});
},
});
}).catch(Hsl.Dialog.showError);
}
queryAll
client.queryAll(entityName: string, query: QueryDefinition, opts?: QueryAllRequestSettings): Promise<WebApiRecord[]>;
This function operates similar to client.query except that it will automatically follow any @odata.nextLink urls and concatenate the results together.
function queryAllSample() {
// Loads all tasks for the specified regarding object.
const client = Hsl.WebApi.getClient('9.1');
return client.queryAll('task', {
filter: '_regardingobjectid_value eq 006ac1c2-b206-45f9-a306-3cbd21417db5',
select: ['subject', 'actualend', 'statecode'],
}, {
formattedValues: true,
timeout: 1000 * 60,
});
}
querySingle
client.querySingle(entityName: string, query: QueryDefinition, opts?: GetRequestSettings): Promise<WebApiRecord>;
A wrapper for client.query that verifies exactly one record is returned and throws an error if no or multiple records exist.
function querySingleSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.querySingle('role', {
select: 'roleid',
filter: `_parentroleid_value eq null and _parentrootroleid_value eq 8A4B077A-9715-4A16-8E7B-2067EBE65A82`,
});
}
get
client.get(url: string, opts?: GetRequestSettings): Promise<any>;
Executes a get request to a pre-built Web API URL. The return will depend on the SAP data loaded.
function getSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.get(`/accounts(35DA53E5-8634-473A-ADC0-D918CCD2B487)?$select=industrycode`, {
formattedValues: true,
});
}
function pagingSample() {
const client = Hsl.WebApi.getClient('9.1');
const requestSettings = {
pageSize: 10,
formattedValues: true,
};
const firstPage = client.query('account', {
select: 'name,industrycode',
filter: `statecode eq 0`,
}, requestSettings).then(resp => {
console.log(`First 10 accounts: ${resp.value.map(x => x.name)}`);
if (resp['@odata.nextLink']) {
// Pass @odata.nextLink to get to query the next page.
return client.get(resp['@odata.nextLink'], requestSettings).then((page2) => {
console.log(`First 10 accounts: ${page2.value.map(x => x.name)}`);
});
}
return undefined;
});
}
bulkRetrieveByIds
client.bulkRetrieveByIds(entityName: string, ids: string[], columns: string[], expand?: ExpandDefinition, _opts?: BulkRetrieveByIdsRequestSettings): Promise<WebApiRecord[]>
interface BulkRetrieveByIdsRequestSettings extends GetRequestSettings {
/** The number of records retrieved per request. */
batchSize?: number;
/** What to do if no record is found with the given id */
notFoundHandling?: 'ignore' | 'error' | 'error-after-all-batches';
}
Retrieves a set of records by primary id. Depending on the number of records, this will actually execute several requests depending on the batch size.
⚠️ Consider whether it may be more efficient to load this data using joins or something similar before using this method.
function bulkRetrieveByIdsSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.bulkRetrieveByIds('account', ['{8787359E-1F6A-46BC-B3A0-11265DE2F083}', '{2AC4DE3F-68CA-49B8-A787-C5FC7B661896}', '{367DED4B-6C44-4E6A-BB21-D1F773592B1C}'], ['name', 'industrycode'], undefined, {
formattedValues: true,
});
}
fetchAllPages
client.fetchAllPages(fetchXml: string | XMLDocument, _opts?: GetRequestSettings): AsyncGenerator<WebApiRecord[], void, void>
Returns the pages for the specified query one-by-one using async iteration.
⚠️ This method will fail in IE11 unless Symbol.asyncIterator is polyfilled. You'll also need to use tooling like TypeScript or Babel to downlevel for-await-of loops in order to support IE11. For cases where that isn't practical and IE support is needed, use fetchAll instead or page through the records using @Microsoft.Dynamics.CRM.fetchxmlpagingcookie.
async function fetchAllPagesSample() {
// Loads all tasks for the specified regarding object.
const client = Hsl.WebApi.getClient('9.1');
const pages = client.fetchAllPages(`<fetch>
<entity name="task">
<attribute name="subject" />
<filter>
<condition attribute="regardingobjectid" operator="eq" value="006ac1c2-b206-45f9-a306-3cbd21417db5" />
</filter>
</entity>
</fetch>`, {
formattedValues: true,
timeout: 1000 * 60,// 60 seconds
});
for await (const page of pages) {
// Each page is an array of WebApiRecords. The page size will be 5000 by default.
console.log(page);
}
}
queryAllPages
client.queryAllPages(entityName: string, query: QueryDefinition, _opts?: GetRequestSettings): AsyncGenerator<WebApiRecord[], void, void>
Returns the pages for the specified query one-by-one using async iteration.
⚠️ This method will fail in IE11 unless Symbol.asyncIterator is polyfilled. You'll also need to use tooling like TypeScript or Babel to downlevel for-await-of loops in order to support IE11. For cases where that isn't practical and IE support is needed, use queryAll instead or page through the records using @odata.nextLink.
async function queryAllPagesSample() {
// Loads all tasks for the specified regarding object.
const client = Hsl.WebApi.getClient('9.1');
const pages = client.queryAllPages('task', {
filter: '_regardingobjectid_value eq 006ac1c2-b206-45f9-a306-3cbd21417db5',
select: ['subject', 'actualend', 'statecode'],
}, {
formattedValues: true,
timeout: 1000 * 60,// 60 seconds
});
for await (const page of pages) {
// Each page is an array of WebApiRecords. The page size will be 5000 by default.
console.log(page);
}
}
downloadFile
client.downloadFile(recordReference: WebApiReference, attributeName: string): Promise<FileInfo>;
Downloads a File attribute type value to the browser. The return from this method will be a Blob with 'name' set to the filename in IE and a File object (which extends the Blob object) in all other browsers. (IE11 doesn�t support the file constructor).
This method transparently supports chunking large files when needed.
⚠️ This method only downloads the file to the client as a Blob. See http://stackoverflow.com/a/33542499 for a way to download it to the user's file system.
function downloadFile() {
const client = Hsl.WebApi.getClient('9.1');
return client.downloadFile({ entityType: 'hsl_quote', id: '{97767165-7EB8-4885-BECD-DD5A7D4A8EB1}' }, 'hsl_textfile').then(function (file) {
console.log(file.name);
return new Promise(function (resolve) {
const reader = new FileReader();
reader.addEventListener('loadend', function () {
resolve(reader.result);
});
reader.readAsText(file);
});
});
}
Functions and Actions
function
client.function(settings: ExecuteSettings): Promise<WebApiParameters>;
Executes the specified function.
// See list of functions here https://msdn.microsoft.com/en-us/library/mt607866.aspx
function getPrivilegesToCurrentUser() {
const client = Hsl.WebApi.getClient('9.0');
const globalContext = Xrm.Utility.getGlobalContext();
// See https://msdn.microsoft.com/en-us/library/mt607996.aspx
// Bound function
return client.function({
name: 'RetrieveUserPrivileges',
binding: { entityType: 'systemuser', id: globalContext.getUserId() }
});
}
function customAction() {
const client = Hsl.WebApi.getClient('9.0');
// Unbound custom action
client.action({
name: 'new_CreateContact',
parameters: { LastName: 'Test' },
});
}
function testGlobalAction() {
const client = Hsl.WebApi.getClient('9.0');
// Unbound custom action
client.action({
name: 'new_GlobalAction',
parameters: {
InputParameter1: '432',
DateInput: new Date(2014, 1, 2)
},
});
}
function boundAction() {
const client = Hsl.WebApi.getClient('9.0');
// Bound custom action
return client.action({
name: 'new_GetAccountName',
binding: { entityType: 'account', id: '{FC7E9EE3-2840-4EA6-A035-E693C7703958}' },
parameters: { GetNumberInstead: true },
});
}
function functionWithGuidParameter() {
const client = Hsl.WebApi.getClient('9.0');
const parameters = Hsl.WebApi.parameterBuilder();
parameters.setGuid('ComponentId', '70816501-edb9-4740-a16c-6a5efbc05d84' /*Account entity's MetadataId */);
parameters.set('ComponentType', 1 /*Entity*/);
return client.function({
name: 'IsComponentCustomizable',
parameters: parameters,
});
}
function functionWithEnumParameter() {
const client = Hsl.WebApi.getClient('9.0');
const parameters = Hsl.WebApi.parameterBuilder();
// Bit of a pain but we have to specify the enum name in addition to the value.
parameters.setEnum('AccessType', 'Microsoft.Dynamics.CRM.EndpointAccessType', 'Default');
return client.function({
name: 'RetrieveCurrentOrganization',
parameters,
}).then(resp => {
console.log(resp.Detail);
});
}
function actionWithEnumParameter() {
const client = Hsl.WebApi.getClient('9.0');
return client.action({
name: 'ReplacePrivilegesRole',
binding: {
entityType: 'role',
id: '{80620E54-7CA1-4B00-99D2-807EE3AA2D17}',
},
parameters: {
Privileges: [{
Depth: 'Deep',
PrivilegeId: '{3D58ABC0-6352-4D38-91E4-5C3CC7B7BB10}',
BusinessUnitId: '{051F453C-D1BA-4CCB-8F0B-CCB351392719}',
}],
},
});
}
async function entityTypeParameter() {
const client = Hsl.WebApi.getClient('9.0');
const parameters = Hsl.WebApi.parameterBuilder();
parameters.setEnum('TargetFieldType', 'Microsoft.Dynamics.CRM.TargetFieldType', 'All');
parameters.setEntityType('EntityMoniker', { entityType: 'account', id: '{229AB137-4D49-445B-8C16-46D22F46F68E}' });
parameters.set('TargetEntityName', 'contact');
return client.function({
name: 'InitializeFrom',
parameters,
});
}
function composableFunction() {
const client = Hsl.WebApi.getClient('9.0');
const resourceName = 'hsl_/jslib/v9/library.core.js';
// https://msdn.microsoft.com/en-us/library/mt683541.aspx
return client.function({
name: 'RetrieveUnpublishedMultiple',
binding: 'webresource',
query: {
filter: `name eq ${Hsl.WebApi.encodeString(resourceName)}`,
select: ['name', 'content_binary'],
},
});
}
Upserts a record by alternate key then returns the record.
function upsertSample() {
const client = Hsl.WebApi.getClient('9.1');
// Assumes accountnumber has been set up as an alternate key for the account entity.
return client.upsert({ entityType: 'account', accountnumber: '123' }, {
name: '123 Company',
telephone1: '814-555-1234',
});
}
action
client.action(settings: ExecuteSettings): Promise<WebApiParameters>;
Executes the specified action.
function entityTypeParameterExample() {
const client = Hsl.WebApi.getClient('9.0');
return client.action({
name: 'RouteTo',
parameters: {
Target: Hsl.WebApi.encodeEntityType('systemuser', '{071B9BCC-ADCB-42E5-8537-4102D63B2097}'),
QueueItem: Hsl.WebApi.encodeEntityType('queueitem', 'b5f3049c-46d7-45e0-b80b-b2e73ac059bc'),
}
});
}
function runWorkflowOnRecord(workflowId, recordId) {
const client = Hsl.WebApi.getClient('9.0');
return client.action({
name: 'ExecuteWorkflow',
parameters: {
EntityId: recordId,
},
binding: {
entityType: 'workflow',
id: workflowId,
},
});
}
function runAction(relatedUserId, requestIds) {
const client = Hsl.WebApi.getClient('9.0');
client.action({
name: 'hsl_SubscribeActions',
parameters: {
Name: 'TagContacts',
InData: JSON.stringify({
contactIds: requestIds,
owningUser: relatedUserId,
}),
},
}).then(resp => {
const result = JSON.parse(resp.OutData);
});
}
function entityTypeWithFieldsActionExample() {
const client = Hsl.WebApi.getClient('9.0');
return client.action({
name: 'CloseIncident',
parameters: {
IncidentResolution: Hsl.WebApi.encodeEntityType('incidentresolution', null, {
subject: 'Resolve Case',
incidentid: Hsl.WebApi.bindReference({
entityType: 'incident',
id: '{3B68BC21-B7BF-418F-8CD8-1525A6E8533C}',
}),
}),
Status: 5,
},
});
}
executeJsonService
client.action(settings: ExecuteSettings): Promise<WebApiParameters>;
Executes the specified custom action using Hitachi's JSON service conventions. Often used with Hitachi's JsonServicePlugin class. Json services can be especially useful for scenarios with structured data in the request and/or response that can't be easily represented by the simple datatypes available for custom actions.
interface ExecuteJsonServiceSettings extends RequestSettings {
actionName: string;
operationName: string;
data: any;
}
actionName
- the name of the custom action to execute.operationName
specifies which operation to rundata
will be serialized to JSON and passed to the plugin.
See RequestSettings
function executeJsonServiceSample() {
const client = Hsl.WebApi.getClient('9.1');
return client.executeJsonService({
actionName: 'hsl_MyCustomAction',
operationName: 'SendUpdatedContactInfoToErpSystem',
data: {
phones: [{
type: 'mobile',
value: '814-555-1234',
}, {
type: 'home',
value: '864-555-1234',
}],
emails: [{
value: 'test@example.com',
}],
},
}).then(resp => {
console.log(resp.phones[0].success);
console.log(resp.phones[1].success);
console.log(resp.emails[0].success);
});
}