'use strict';
var fs = require('fs');
var request = require('request-promise');
var jwtDecode = require('jwt-decode');
var User = require('./structures/user');
var Ticket = require('./structures/ticket');
var Account = require('./structures/account');
var Location = require('./structures/location');
var Asset = require('./structures/asset');
var Article = require('./structures/article');
/**
* Base TeamDynamix API class
* @class
* @constructor
* @param {TDAPIOptions} options - The options to configure the TDAPI client with.
*/
function TDAPI(options) {
this.credentials = options.credentials || { UserName: '', Password: '' };
this.baseUrl = options.baseUrl || 'https://api.teamdynamix.com/TDWebAPI/api';
this.bearerToken = '';
}
/**
*
* @typedef {Object} TDAPIOptions
* @property {String} baseUrl - The base URL of your TeamDynamix API (i.e. https://api.teamdynamix.com/TDWebAPI/api)
* @property {APIUser|AdminUser} credentials - The API User or Admin User to authenticate as.
*/
/**
* @typedef {Object} APIUser
* @property {String} UserName - The username to authenticate as.
* @property {String} Password - The user's password.
*/
/**
* @typedef {Object} AdminUser
* @property {String} BEID - The BEID.
* @property {String} WebServicesKey - The web services key.
*/
/**
* Gets a Bearer Token for authenticating other requests.
* @returns {Promise<String>}
*/
TDAPI.prototype.login = function () {
return new Promise((resolve, reject) => {
if (this.bearerToken && !tokenExpired(this.bearerToken)) {
resolve(this.bearerToken);
} else {
var options = {
method: 'POST',
url: `${this.baseUrl}/auth` + ('BEID' in this.credentials ? '/loginadmin' : '/login'),
form: this.credentials
};
request(options)
.then(bearerToken => {
resolve(bearerToken);
})
.catch(err => {
reject(err);
});
}
});
};
/**
* Gets a user object.
* @param {Guid} uid - The UID of the user.
* @returns {Promise<User>}
*/
TDAPI.prototype.getUser = function (uid) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/people/${uid}`,
auth: { bearer: bearerToken },
json: true
});
})
.then(user => new User(this, user))
.catch(handleError);
};
/**
* Gets all user objects matching specified search parameters.
* @param {UserSearch} searchParams - The search parameters to use.
* @returns {Promise<User[]>}
*/
TDAPI.prototype.getUsers = function (searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.then(users => {
if (Array.isArray(users)) {
return users.map(user => new User(this, user));
} else {
return users;
}
})
.catch(handleError);
};
/**
* Creates a user object with specified attributes.
* @param {User} user - The user to create.
* @returns {Promise<User>}
*/
TDAPI.prototype.createUser = function (user) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people`,
auth: { bearer: bearerToken },
json: true,
body: user
});
})
.then(user => new User(this, user))
.catch(handleError);
};
/**
* Gets a Security Role.
* @param {Guid} roleId - The ID of the security role.
* @returns {Promise<SecurityRole>} securityRole
*/
TDAPI.prototype.getSecurityRole = function (roleId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/securityroles/${roleId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets Security Roles.
* @param {SecurityRoleSearch} searchParams - The search parameters to use.
* @returns {Promise<SecurityRole[]>} securityRoles
*/
TDAPI.prototype.getSecurityRoles = function (searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/securityroles/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.catch(handleError);
};
/**
* Gets a Group.
* @param {Number} id - The group ID.
* @returns {Promise<Group>} The group.
*/
TDAPI.prototype.getGroup = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/groups/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Creates a new group.
* @param {Group} group - The group to be created.
* @returns {Promise<Group>} The group.
*/
TDAPI.prototype.createGroup = function (group) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/groups`,
auth: { bearer: bearerToken },
json: true,
body: group || {}
});
})
.catch(handleError);
};
/**
* Edits an existing group.
* @param {Number} id - The group ID.
* @param {Group} group - The fields that the updated group should hold.
* @returns {Promise<Group>} The updated group, if the operation was successful.
*/
TDAPI.prototype.editGroup = function (id, group) {
return this.login()
.then(bearerToken => {
return request({
method: 'PUT',
url: `${this.baseUrl}/groups/${id}`,
auth: { bearer: bearerToken },
json: true,
body: group || {}
});
})
.catch(handleError);
};
/**
* Removes a collection of users from a group.
* @param {Number} id - The group ID.
* @param {Guid[]} uids - The search parameters to use.
* @returns {Promise<Object>} A response message indicating if the operation was successful or not.
*/
TDAPI.prototype.removeFromGroup = function (id, uids) {
return this.login()
.then(bearerToken => {
return request({
method: 'DELETE',
url: `${this.baseUrl}/groups/${id}/members`,
auth: { bearer: bearerToken },
json: true,
body: uids || []
});
})
.catch(handleError);
};
/**
* Gets Groups.
* @param {GroupSearch} [searchParams={}] - The search parameters to use.
* @returns {Promise<Group[]>} A collection of groups.
*/
TDAPI.prototype.getGroups = function (searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/groups/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.catch(handleError);
};
/**
* Gets group members for a specified group.
* @param {Number} groupId - The ID of the group.
* @returns {Promise<User[]>} groupMembers
*/
TDAPI.prototype.getGroupMembers = function (groupId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/groups/${groupId}/members`,
auth: { bearer: bearerToken },
json: true
});
})
.then(users => {
if (Array.isArray(users)) {
return users.map(user => new User(this, user));
} else {
return users;
}
})
.catch(handleError);
};
/**
* Bulk-applies a desktop template to a set of users.
* @param {Guid} templateDesktopId - The ID of the desktop template to apply.
* @param {Guid[]} uids -The UIDs of the users to apply the desktop to.
* @param {Boolean} [isDefault=false] - If set to true, each of the specified users will be set to be active.
*/
TDAPI.prototype.applyDesktop = function (templateDesktopId, uids, isDefault) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/bulk/applydesktop/${templateDesktopId}?isDefault=${isDefault || false}`,
auth: { bearer: bearerToken },
json: true,
body: uids || []
});
})
};
/**
* Bulk-updates the active status of the set of users.
* @param {Guid} uids - The UIDs of the people to update the active status of.
* @param {Boolean} isActive - If set to true, each of the specified users will be set to be active.
*/
TDAPI.prototype.changeActiveStatus = function (uids, isActive) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/bulk/changeactivestatus?isActive=${isActive || false}`,
auth: { bearer: bearerToken },
json: true,
body: uids || []
});
})
};
/**
* Bulk-adds or removes a set of users to a set of applications. Optionally, supports removing any application associations for users.
* @param {Guid[]} userUids - The UIDs of the people being added to entries in applicationsNames.
* @param {String[]} applicationNames - The Applications that will be added to each entry in userUids.
* @param {Boolean} replaceExistingApplications - Value indicating whether applications that provided users already belong to should be removed.
*/
TDAPI.prototype.changeApplications = function (userUids, applicationNames, replaceExistingApplications) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/bulk/changeapplications`,
auth: { bearer: bearerToken },
json: true,
body: {
UserUids: userUids || [],
ApplicationNames: applicationNames || [],
ReplaceExistingApplications: replaceExistingApplications || false
}
});
})
};
/**
* Bulk-adds a set of users to a set of groups. Optionally, supports removing any memberships for those users that are outside of those groups.
* @param {Guid[]} userUids - The UIDs of the people being added to entries in groupIds.
* @param {Number[]} groupIds - The groups that will be added to each entry in userUids.
* @param {Boolean} removeOtherGroups - Value indicating whether groups that provided users already belong to should be removed.
*/
TDAPI.prototype.manageGroups = function (userUids, groupIds, removeOtherGroups) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/bulk/managegroups`,
auth: { bearer: bearerToken },
json: true,
body: {
UserUIDs: userUids || [],
GroupIDs: groupIds || [],
RemoveOtherGroups: removeOtherGroups || false
}
});
})
};
/**
* Bulk-adds a set of users to a group. Adds a collection of users to a group.
* Users that did not exist in the group beforehand will have their settings set to the specified values.
* Existing users will not have their settings overwritten.
* @param {Boolean} id - ID of the group
* @param {Guid[]} uids - The UIDs of the people being added to entries in groupIds.
* @param {Boolean} [isPrimary=false] - If set to true, new users will have this group set as their primary group.
* @param {Boolean} [isNotified=false] - If set to true, new users will be sent notifications for this group.
* @param {Boolean} [isManager=false] - If set to true, new users will be set as a manager for this group.
*/
TDAPI.prototype.addUsersToGroup = function (id, uids, isPrimary, isNotified, isManager) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/groups/${id}/members?isPrimary=${isPrimary}&isNotified=${isNotified}&isManager=${isManager}`,
auth: { bearer: bearerToken },
json: true,
body: uids || []
});
})
};
/**
* Bulk-adds a set of users to a set of accounts. Optionally, supports removing any accounts for the specified users that are not included in the set of accounts.
* @param {Guid[]} userUids - The user UIDs to add to the accounts provided in AccountIDs
* @param {Number[]} accountIds - The account IDs to add the users provided in userUIDs to.
* @param {Boolean} replaceExistingAccounts - Value indicating whether accounts that provided users already belong to should be removed.
*/
TDAPI.prototype.changeAcctDepts = function (userUids, accountIds, replaceExistingAccounts) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/bulk/changeacctdepts`,
auth: { bearer: bearerToken },
json: true,
body: {
UserUids: userUids || [],
AccountIDs: accountIds || [],
ReplaceExistingAccounts: replaceExistingAccounts || false
}
});
})
};
/**
* Bulk-changes the security role of a set of users.
* @param {Guid} securityRoleId - The ID of the security role to apply to each user.
* @param {Guid[]} uids - The groups that will be added to each entry in userUids.
*/
TDAPI.prototype.changeSecurityRole = function (securityRoleId, uids) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/bulk/changesecurityrole/${securityRoleId} `,
auth: { bearer: bearerToken },
json: true,
body: uids || []
});
})
};
/**
* Creates a Ticket.
* @param {Number} appId - The ID of the ticketing application.
* @param {any} ticket - The ticket body
* @param {TicketCreateOptions} options - The creation options
*/
TDAPI.prototype.createTicket = function (appId, ticket, options) {
return this.login()
.then(bearerToken => {
if (!options) {
options = {
EnableNotifyReviewer: false,
NotifyRequestor: false,
NotifyResponsible: false,
AutoAssignResponsibility: false,
AllowRequestorCreation: false
};
}
return request({
method: 'POST',
url: `${this.baseUrl}/${appId}/tickets?` +
`EnableNotifyReviewer=${options.EnableNotifyReviewer || false}` +
`&NotifyRequestor=${options.NotifyRequestor || false}` +
`&NotifyResponsible=${options.NotifyResponsible || false}` +
`&AutoAssignResponsibility=${options.AutoAssignResponsibility || false}` +
`&AllowRequestorCreation=${options.AllowRequestorCreation || false}`,
auth: { bearer: bearerToken },
json: true,
body: ticket || {}
});
})
.then(ticket => new Ticket(this, ticket))
.catch(handleError);
};
/**
* Gets a Ticket.
* @param {Number} appId - The ID of the ticketing application.
* @param {Number} ticketId - The ID of the ticket.
* @returns {Promise<Ticket>} ticket
*/
TDAPI.prototype.getTicket = function (appId, ticketId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/${appId}/tickets/${ticketId}`,
auth: { bearer: bearerToken },
json: true
});
})
.then(ticket => new Ticket(this, ticket))
.catch(handleError);
};
/**
* Gets Tickets.
* @param {Number} appId - The ID of the ticketing application.
* @param {TicketSearch} [searchParams={}] - The search parameters to use.
* @returns {Promise<Ticket[]>}
*/
TDAPI.prototype.getTickets = function (appId, searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/${appId}/tickets/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.then(tickets => {
if (Array.isArray(tickets)) {
return tickets.map(ticket => new Ticket(this, ticket));
} else {
return tickets;
}
})
.catch(handleError);
};
/**
* Edits a ticket via HTTP PATCH (Edits only specified fields)
* @param {Number} appId - The ID of the ticketing application.
* @param {Number} ticketId - The ID of the ticket to update.
* @param {any} patch - The patch document containing changes to apply to the ticket.
* @param {Boolean} [false] notifyNewResponsible - If true, will notify the newly-responsible resource(s) if the responsibility is changed as a result of the edit.
* @returns {Promise<Ticket>} ticket
*/
TDAPI.prototype.patchTicket = function (appId, ticketId, patch, notifyNewResponsible) {
return this.login()
.then(bearerToken => {
return request({
method: 'PATCH',
url: `${this.baseUrl}/${appId}/tickets/${ticketId}?notifyNewResponsible=${notifyNewResponsible || false}`,
auth: { bearer: bearerToken },
json: true,
body: patch
});
})
.catch(handleError);
};
/**
* Updates a ticket / Adds a new feed entry.
* @param {Number} appId - The ID of the ticketing application.
* @param {Number} ticketId - The ID of the ticket to update.
* @param {TicketFeedEntry} feedEntry - The new feed entry to add.
* @returns {Promise<ItemUpdate>} itemUpdate
*/
TDAPI.prototype.updateTicket = function (appId, ticketId, feedEntry) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/${appId}/tickets/${ticketId}/feed`,
auth: { bearer: bearerToken },
json: true,
body: feedEntry
});
})
.catch(handleError);
};
/**
* Gets all active ticket types
* @param {Number} appId - The ID of the ticketing application.
* @returns {Promise<TicketType[]>} types
*/
TDAPI.prototype.getTicketTypes = function (appId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/${appId}/tickets/types`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Get ticket statuses.
* @param {Number} appId - The ID of the ticketing application.
* @returns {Promise<TicketStatus[]>} statuses
*/
TDAPI.prototype.getTicketStatuses = function (appId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/${appId}/tickets/statuses`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets a list of all active accounts/departments.
* @returns {Promise<Account[]>} accounts
*/
TDAPI.prototype.getAccounts = function () {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/accounts`,
auth: { bearer: bearerToken },
json: true
});
})
.then(accounts => {
if (Array.isArray(accounts)) {
return accounts.map(account => new Account(this, account));
} else {
return accounts;
}
})
.catch(handleError);
};
/**
* Gets the account specified by the account ID.
* @param {Number} id - The ID of the account.
* @returns {Promise<Account>} account
*/
TDAPI.prototype.getAccount = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/accounts/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.then(account => new Account(this, account))
.catch(handleError);
};
/**
* Creates a new account.
* @param {Account} account - The account to be created.
* @returns {Promise<Account>} account
*/
TDAPI.prototype.createAccount = function (account) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/accounts`,
json: true,
body: account || {}
});
})
.then(account => new Account(this, account))
.catch(handleError);
};
/**
* Edits the account specified by the account ID.
* @param {Number} id - The account ID.
* @param {Account} account - The fields that the updated account should hold.
* @returns {Promise<Object>} body
*/
TDAPI.prototype.editAccount = function (id, account) {
return this.login()
.then(bearerToken => {
return request({
method: 'PUT',
auth: { bearer: bearerToken },
json: true,
body: account || {}
});
})
.catch(handleError);
};
/**
* Gets a list of accounts/departments
* @param {AccountSearch} searchParams
* @returns {Promise<Account[]>} accounts
*/
TDAPI.prototype.searchAccounts = function (searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/accounts/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.then(accounts => {
if (Array.isArray(accounts)) {
return accounts.map(account => new Account(this, account));
} else {
return accounts;
}
})
.catch(handleError);
};
/**
* Creates a location.
* @param {Location} location - The location to create.
* @returns {Promise<Location>} location
*/
TDAPI.prototype.createLocation = function (location) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/locations`,
auth: { bearer: bearerToken },
json: true,
body: location
});
})
.then(location => new Location(this, location))
.catch(handleError);
};
/**
* Gets a location.
* @param {Number} id - The ID of the location.
* @returns {Promise<Location>} location
*/
TDAPI.prototype.getLocation = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/locations/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.then(location => new Location(this, location))
.catch(handleError);
};
/**
* Edits the specified location.
* @param {Number} id - The ID of the location.
* @param {Location} location - The location with updated values.
* @returns {Promise<Location>} location
*/
TDAPI.prototype.editLocation = function (id, location) {
return this.login()
.then(bearerToken => {
return request({
method: 'PUT',
url: `${this.baseUrl}/locations/${id}`,
auth: { bearer: bearerToken },
json: true,
body: location
});
})
.then(location => new Location(this, location))
.catch(handleError);
};
/**
* Creates a room in a location.
* @param {Number} id - The containing location ID.
* @param {LocationRoom} room - The room to create.
* @returns {Promise<LocationRoom>} room
*/
TDAPI.prototype.createRoom = function (id, room) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/locations/${id}/rooms`,
auth: { bearer: bearerToken },
json: true,
body: room
});
})
.catch(handleError);
};
/**
* Deletes a room in a location.
* @param {Number} id - The containing location ID.
* @param {Number} roomId - The room ID.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.deleteRoom = function (id, roomId) {
return this.login()
.then(bearerToken => {
return request({
method: 'DELETE',
url: `${this.baseUrl}/locations/${id}/rooms/${roomId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Edits the specified room in a location.
* @param {Number} id - The containing location ID.
* @param {Number} roomId - The room ID.
* @param {LocationRoom} room - The room with updated values.
* @returns {Promise<LocationRoom>} room
*/
TDAPI.prototype.editRoom = function (id, roomId, room) {
return this.login()
.then(bearerToken => {
return request({
method: 'PUT',
url: `${this.baseUrl}/locations/${id}/rooms/${roomId}`,
auth: { bearer: bearerToken },
json: true,
body: room
});
})
.catch(handleError);
};
/**
* Gets a list of locations.
* @param {LocationSearch} searchParams - The search parameters to use.
* @returns {Promise<Location[]>} locations
*/
TDAPI.prototype.getLocations = function (searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/locations/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.catch(handleError);
};
/**
* Accepts a file, stores that file on disk, and places an entry into the databse to indicate to the import file processor to pick up the file and run a People import on it.
* @param {String} file - The path of the .xlsx file to upload.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.importPeople = function (file) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/people/import`,
auth: { bearer: bearerToken },
formData: {
'import.xlsx': fs.createReadStream(file)
}
});
})
.catch(handleError);
};
/**
* Gets the custom attributes for the specified component.
* @param {Number} componentId - The component ID.
* @param {Number} associatedTypeId - The associated type ID to get attributes for. For instance, a ticket type ID might be provided here.
* @param {Number} appId - The associated application ID to get attributes for.
* @returns {Promise<CustomAttribute[]>}
*/
TDAPI.prototype.getCustomAttributes = function (componentId, associatedTypeId, appId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/attributes/custom` +
`?componentId=${componentId}` +
`&associatedTypeId=${associatedTypeId}` +
`&appId=${appId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets a list of all asset statuses.
* @returns {Promise<AssetStatus[]>}
*/
TDAPI.prototype.getAssetStatuses = function () {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/statuses`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Creates an asset.
* @param {Asset} asset
* @returns {Promise<Asset>}
*/
TDAPI.prototype.createAsset = function (asset) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets`,
auth: { bearer: bearerToken },
json: true,
body: asset
});
})
.then(asset => new Asset(this, asset))
.catch(handleError);
};
/**
* Removes a resource from an asset.
* @param {Number} assetId - The asset ID.
* @param {Number} resourceId - The resource ID.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.removeAssetResource = function (assetId, resourceId) {
return this.login()
.then(bearerToken => {
return request({
method: 'DELETE',
url: `${this.baseUrl}/assets/${assetId}/users/${resourceId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets an asset.
* @param {Number} id - The asset ID.
* @returns {Promise<Asset>}
*/
TDAPI.prototype.getAsset = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.then(asset => new Asset(this, asset))
.catch(handleError);
};
/**
* Edits an existing asset.
* @param {Number} id - The asset ID.
* @param {Asset} asset - The asset with updated values.
* @returns {Promise<Asset>}
*/
TDAPI.prototype.editAsset = function (id, asset) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/${id}`,
auth: { bearer: bearerToken },
json: true,
body: asset
});
})
.then(asset => new Asset(this, asset))
.catch(handleError);
};
/**
* Gets the feed entries for an asset.
* @param {Number} id - The asset ID.
* @returns {Promise<ItemUpdate[]>}
*/
TDAPI.prototype.getAssetFeedEntries = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/${id}/feed`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Adds a comment to an asset.
* @param {Number} id - The asset ID.
* @param {FeedEntry} feedEntry - The item update containing the comment.
* @returns {Promise<ItemUpdate>}
*/
TDAPI.prototype.addAssetFeedEntry = function (id, feedEntry) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/${id}/feed`,
auth: { bearer: bearerToken },
json: true,
body: feedEntry
});
})
.catch(handleError);
};
/**
* Adds an asset to a ticket.
* @param {Number} id - The asset ID.
* @param {Number} ticketId - The ticket ID. This must belong to an application that the user can access.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.addAssetToTicket = function (id, ticketId) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/${id}/tickets/${ticketId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Get assets associated with a ticket.
* The endpoint also returns additional configuration items.
* @param {Number} appId - The associated application ID to get attributes for.
* @param {Number} ticketId - The ticket ID. This must belong to an application that the user can access.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.getAssetsFromTicket = function (appId, ticketId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/${appId}/tickets/${ticketId}/assets`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
}
/**
* Removes a ticket from an asset.
* @param {Number} id - The asset ID.
* @param {Number} ticketId - The ticket ID. This must belong to an application that the user can access.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.removeAssetFromTicket = function (id, ticketId) {
return this.login()
.then(bearerToken => {
return request({
method: '',
url: `${this.baseUrl}/assets/${id}/tickets/${ticketId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets the asset resources.
* @param {Number} id - The asset ID.
* @returns {Promise<ResourceItem>}
*/
TDAPI.prototype.getAssetResources = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/${id}/users`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Adds a resource to an asset.
* @param {Number} id - The asset ID.
* @param {Number} resourceId - The resource ID.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.addAssetResource = function (id, resourceId) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/${id}/users/${resourceId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Performs a bulk insert/update of assets in the system.
* @param {BulkImport<Asset[]>} importData - The collection of items that are being imported and the corresponding import settings.
* @returns {Promise<ItemResult>}
*/
TDAPI.prototype.importAssets = function (importData) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/import`,
auth: { bearer: bearerToken },
json: true,
body: importData
});
})
.catch(handleError);
};
/**
* Gets a list of assets.
* @param {AssetSearch} [searchParams] - The search parameters to use.
* @returns {Promise<Asset[]>}
*/
TDAPI.prototype.getAssets = function (searchParams) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
})
.then(assets => {
if (Array.isArray(assets)) {
return assets.map(asset => new Asset(this, asset));
} else {
return assets;
}
})
.catch(handleError);
};
/**
* Gets a list of active vendors.
* @returns {Promise<Vendor[]>}
*/
TDAPI.prototype.getVendors = function () {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/vendors`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets a list of vendors.
* @param {VendorSearch} query - The searching parameters to use
* @returns {Promise<Vendor[]>}
*/
TDAPI.prototype.searchVendors = function (query) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/vendors/search`,
auth: { bearer: bearerToken },
json: true,
body: query || {}
});
})
.catch(handleError);
};
/**
* Gets a vendor.
* @param {Number} id - The vendor ID.
* @returns {Promise<Vendor>}
*/
TDAPI.prototype.getVendor = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/vendors/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Creates a new vendor.
* @param {Vendor} vendor - The vendor to be created.
* @returns {Promise<Vendor>}
*/
TDAPI.prototype.createVendor = function (vendor) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/vendors`,
auth: { bearer: bearerToken },
json: true,
body: vendor || {}
});
})
.catch(handleError);
};
/**
* Gets a list of active product models.
* @returns {Promise<ProductModel[]>}
*/
TDAPI.prototype.getProductModels = function () {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/models`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets the specified product model.
* @param {Number} id
* @returns {Promise<ProductModel>}
*/
TDAPI.prototype.getProductModel = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/assets/models/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Creates a new product model.
* @param {ProductModel} productModel
* @returns {ProductModel} - The new Product Model.
*/
TDAPI.prototype.createProductModel = function (productModel) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/assets/models`,
auth: { bearer: bearerToken },
json: true,
body: productModel || {}
});
})
.catch(handleError);
};
/**
* Edits a product model.
* @param {ProductModel} productModel - The locally edited product model
* @returns {Promise<ProductModel>} - The updated Product Model in TDx
*/
TDAPI.prototype.editProductModel = function (productModel) {
return this.login()
.then(bearerToken => {
return request({
method: 'PUT',
url: `${this.baseUrl}/assets/models/${productModel.ID}`,
auth: { bearer: bearerToken },
json: true,
body: productModel || {}
});
})
.catch(handleError);
};
/**
* Gets the choices for the specified custom attribute.
* @param {Number} id - The ID of the custom attribute.
* @returns {Promise<CustomAttributeChoice[]>}
*/
TDAPI.prototype.getCustomAttributeChoices = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/attributes/${id}/choices`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Adds a new choice to the specified custom attribute.
* @param {Number} id - The ID of the custom attribute.
* @param {CustomAttributeChoice} customAttributeChoice - The choice to add to the custom attribute.
* @returns {Promise<CustomAttributeChoice>}
*/
TDAPI.prototype.addCustomAttributeChoice = function (id, customAttributeChoice) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/attributes/${id}/choices`,
auth: { bearer: bearerToken },
json: true,
body: customAttributeChoice || {}
});
})
.catch(handleError);
};
/**
* Removes the specified choice from the custom attribute.
* @param {Number} id - The custom attribute ID.
* @param {any} choiceId - The choice ID.
* @returns {Promise<Object>} message
*/
TDAPI.prototype.removeCustomAttributeChoice = function (id, choiceId) {
return this.login()
.then(bearerToken => {
return request({
method: 'DELETE',
url: `${this.baseUrl}/attributes/${id}/${choiceId}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Edits an existing choice associated with the specified custom attribute.
* @param {Number} id - The custom attribute ID.
* @param {Number} choiceId - The choice ID.
* @param {CustomAttributeChoice} customAttributeChoice - The choice with updated values.
* @returns {Promise<CustomAttributeChoice>}
*/
TDAPI.editCustomAttributeChoice = function (id, choiceId, customAttributeChoice) {
return this.login()
.then(bearerToken => {
return request({
method: 'PUT',
url: `${this.baseUrl}/attributes/${id}/${choiceId}`,
auth: { bearer: bearerToken },
json: true,
body: customAttributeChoice || {}
});
})
.catch(handleError);
};
/**
* Gets the custom attributes for the specified component.
* @param {Number} componentId - The component ID.
* @param {Number} [associatedTypeId] - The associated type ID to get attributes for. For instance, a ticket type ID might be provided here.
* @param {Number} [appId] - The associated application ID to get attributes for.
* @returns {Promise<CustomAttribute[]>}
*/
TDAPI.getCustomAttributes = function (componentId, associatedTypeId, appId) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/attributes/custom?` +
`componentId=${componentId}` +
`&associatedTypeId=${associatedTypeId || 0}` +
`&appId=${appId || 0}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets a list of all Report Builder reports visible to the user.
* @returns {Promise<ReportInfo[]>}
*/
TDAPI.prototype.getReports = function () {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/reports`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets information about a report, optionally including data.
* @param {Number} id - The report ID.
* @param {Boolean} [withData] - If true, will populate the returned report's collection of rows.
* @param {String} [dataSortExpression] - The sorting expression to use for the report's data. Only applicable when data is being retrieved. When not provided, will fall back to the default used for the report.
* @returns {Promise<Report>}
*/
TDAPI.prototype.getReport = function (id, withData, dataSortExpression) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/reports/${id}?` +
`withData=${withData || false}` +
`&dataSortExpression=${dataSortExpression || ''}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets a list of all Report Builder reports visible to the user that match the provided search criteria.
* @param {Object} reportSearch - The searching parameters to use.
* @returns {Promise<ReportInfo>}
*/
TDAPI.prototype.searchReports = function (reportSearch) {
return this.login()
.then(bearerToken => {
return request({
method: 'POST',
url: `${this.baseUrl}/reports/search`,
auth: { bearer: bearerToken },
json: true,
body: reportSearch || {}
});
})
.catch(handleError);
};
/**
* Gets an attachment.
* @param {Guid} id - The attachment ID.
* @returns {Attachment} - The attachment object, if found.
*/
TDAPI.prototype.getAttachment = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/attachments/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets the contents of an attachment.
* @param {Guid} id - The attachment ID.
* @returns {Promise<Object>} - The attachment's file contents, if found.
*/
TDAPI.prototype.getAttachmentContents = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'GET',
url: `${this.baseUrl}/attachments/${id}/content`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Deletes an attachment.
* @param {Guid} id - The attachment ID.
* @returns {Promise<Object>} - A response message indicating if the operation was successful or not.
*/
TDAPI.prototype.deleteAttachment = function (id) {
return this.login()
.then(bearerToken => {
return request({
method: 'DELETE',
url: `${this.baseUrl}/attachments/${id}`,
auth: { bearer: bearerToken },
json: true
});
})
.catch(handleError);
};
/**
* Gets an article from Knowledge Base.
* @param {Number} articleID - The article ID.
* @returns {Promise<Article>}
*/
TDAPI.prototype.getArticle = async function (articleID) {
try {
let bearerToken = await this.login();
let data = await request({
method: 'GET',
url: `${this.baseUrl}/knowledgebase/${articleID}`,
auth: { bearer: bearerToken },
json: true,
});
return new Article(this, data);
} catch (err) {
handleError(err);
}
};
/**
* Searches for articles from Knowledge Base.
* @param {ArticleSearch} searchParams - the search parameters for an article
* @returns {Promise<Array<Article>>}
*/
TDAPI.prototype.getArticles = async function (searchParams) {
try {
let bearerToken = await this.login();
return request({
method: 'POST',
url: `${this.baseUrl}/knowledgebase/search`,
auth: { bearer: bearerToken },
json: true,
body: searchParams || {}
});
} catch (err) {
handleError(err);
}
};
/**
* Get a ticket's task
* @param appId - the ID of the ticket application
* @param ticketID - ID of the ticket
* @param taskID - ID of the task
*
* @returns {Promise<Object>}
*/
TDAPI.prototype.getTicketTask = async function (appId, ticketID, taskID) {
try {
let bearerToken = await this.login();
return request({
method: 'GET',
url: `${this.baseUrl}/${appId}/tickets/${ticketID}/tasks/${taskID}`,
auth: { bearer: bearerToken },
json: true,
});
} catch (err) {
handleError(err);
}
}
/**
* Edit a ticket's task
* @param appId - the ID of the ticket application
* @param ticketID - ID of the ticket
* @param taskID - ID of the task
* @param taskData - data of the edited task
*
* @returns {Promise<Object>}
*/
TDAPI.prototype.editTicketTask = async function (appId, ticketID, taskID, taskData) {
try {
let bearerToken = await this.login();
return request({
method: 'PUT',
url: `${this.baseUrl}/${appId}/tickets/${ticketID}/tasks/${taskID}`,
auth: { bearer: bearerToken },
body: taskData,
json: true,
});
} catch (err) {
handleError(err);
}
}
// Generic error handling - TODO: Improve error detail
function handleError(err) {
return Promise.reject(err);
}
// Check if JWT expired
function tokenExpired(bearerToken) {
var decodedToken = jwtDecode(bearerToken);
if (decodedToken.exp) {
var exp = new Date(decodedToken.exp * 1000); // Convert seconds to ms for Date constructor
var now = new Date();
if (exp > now) {
return false;
}
}
return true;
}
module.exports = TDAPI;