@cityssm/avanti-api
Version:
An unofficial wrapper around the Avanti API.
212 lines (211 loc) • 7.94 kB
JavaScript
import Debug from 'debug';
import { DEBUG_NAMESPACE } from './debug.config.js';
import { objectToUrlSearchParameters } from './utilities.js';
const debug = Debug(`${DEBUG_NAMESPACE}:index`);
export const _defaultLatestASSP = false;
export class AvantiApi {
#apiConfiguration;
#accessTokenUrl;
#accessToken;
#accessTokenTimeMillis = 0;
constructor(configuration) {
this.#apiConfiguration = configuration;
this.#accessTokenUrl =
(this.#apiConfiguration.latestASSP ?? _defaultLatestASSP)
? 'https://auth.myavanti.ca/connect/token'
: `https://myavanti.ca/${this.#apiConfiguration.tenant}-api/connect/token`;
}
#getEndpointUrl(apiEndpoint) {
return (this.#apiConfiguration.latestASSP ?? _defaultLatestASSP)
? `https://${this.#apiConfiguration.tenant}.myavanti.ca/api${apiEndpoint}`
: `https://myavanti.ca/${this.#apiConfiguration.tenant}-api${apiEndpoint}`;
}
async #refreshAccessToken() {
this.#accessTokenTimeMillis = Date.now();
const requestObject = Object.assign({
grant_type: 'password',
device_id: this.#apiConfiguration.device_id ??
`node-avanti-api-${Date.now().toString()}`
}, {
client_id: this.#apiConfiguration.client_id,
client_secret: this.#apiConfiguration.client_secret,
username: this.#apiConfiguration.username,
password: this.#apiConfiguration.password,
company: this.#apiConfiguration.company
});
const request = objectToUrlSearchParameters(requestObject);
debug(`Access token request: ${this.#accessTokenUrl}`);
const response = await fetch(this.#accessTokenUrl, {
method: 'post',
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'x-tenant': this.#apiConfiguration.tenant
},
body: request
});
this.#accessToken = (await response.json());
if ((this.#accessToken?.access_token ?? '') === '') {
debug('Error retrieving access token.');
}
debug(`Access Token: ${this.#accessToken.access_token}`);
return this.#accessToken;
}
/**
* Requests an API endpoint excluded from the included functions.
* @param apiEndpoint - ex. '/v1/Employees'
* @param apiOptions - API Options
* @returns API Response
*/
async callApi(apiEndpoint, apiOptions) {
// eslint-disable-next-line @typescript-eslint/naming-convention
let access_token = this.#accessToken?.access_token ?? '';
if (access_token === '' ||
this.#accessTokenTimeMillis + (this.#accessToken?.expires_in ?? 0) <=
Date.now()) {
const accessTokenResponse = await this.#refreshAccessToken();
access_token = accessTokenResponse.access_token;
}
let requestUrl = this.#getEndpointUrl(apiEndpoint);
debug(`API request: ${requestUrl}`);
if (apiOptions.method === 'get') {
requestUrl += `?${objectToUrlSearchParameters(apiOptions.getParameters ?? {}).toString()}`;
}
const fetchOptions = {
method: apiOptions.method,
headers: {
Authorization: `Bearer ${access_token}`
}
};
if (apiOptions.method === 'post') {
fetchOptions.body = JSON.stringify(apiOptions.bodyParameters);
fetchOptions.headers['Content-Type'] =
'application/json';
}
const response = await fetch(requestUrl, fetchOptions);
// eslint-disable-next-line @typescript-eslint/init-declarations
let parsingError;
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const json = (await response.json()) ?? {};
if (typeof json === 'object' &&
(Object.hasOwn(json, 'status') ||
Object.hasOwn(json, 'instance'))) {
return {
success: false,
error: json
};
}
return {
success: true,
response: json
};
}
catch (error) {
debug(response);
parsingError = error;
}
return {
success: false,
error: {
title: 'callApi() error',
status: 600,
detail: parsingError === undefined ? undefined : parsingError.name,
error: parsingError,
instance: apiEndpoint
}
};
}
/**
* List Employees:
* /v1/Employees
* @param parameters - Request parameters
* @returns See https://avanti.stoplight.io/docs/avanti-api/62932b8f232fb-list-employees
*/
async getEmployees(parameters) {
return (await this.callApi('/v1/Employees', {
bodyParameters: parameters,
method: 'post'
}));
}
/**
* List Employee Earning Codes:
* /v1/EmployeeEarningCodes
* @param employeeNumber - Employee number
* @returns See https://avanti.stoplight.io/docs/avanti-api/204d3078d2230-list-employee-earning-codes
*/
async getEmployeeEarningCodes(employeeNumber) {
return (await this.callApi('/v1/EmployeeEarningCodes', {
getParameters: {
empNo: employeeNumber
},
method: 'get'
}));
}
/**
* Get Employee Job Data:
* /v1/EmployeeJobData
* @param employeeNumber - Employee number
* @returns See https://avanti.stoplight.io/docs/avanti-api/a44b4af6f1abd-get-employee-job-data
*/
async getEmployeeJobData(employeeNumber) {
return (await this.callApi('/v1/EmployeeJobData', {
getParameters: {
empNo: employeeNumber
},
method: 'get'
}));
}
/**
* Get Employee Personal Info:
* /v1/PersonalInfo
* @param employeeNumber - Employee number
* @returns See https://avanti.stoplight.io/docs/avanti-api/f851b988b5cf6-get-employee-personal-info
*/
async getEmployeePersonalInfo(employeeNumber) {
return (await this.callApi('/v1/PersonalInfo', {
getParameters: {
empNo: employeeNumber
},
method: 'get'
}));
}
/**
* List Time Entries:
* /v1/TimeManagement/{viewId}/{templateId}
* @param viewId - View Id
* @param templateId - Template Id
* @param parameters - Request parameters
* @returns See https://avanti.stoplight.io/docs/avanti-api/4952dd2917595-list-time-entries
*/
async getTimeEntries(viewId, templateId, parameters) {
return (await this.callApi(`/v1/TimeManagement/${viewId}/${templateId}`, {
getParameters: parameters,
method: 'get'
}));
}
/**
* List Time Entry Templates:
* /v1/TimeManagement/Templates
* @param parameters - Request parameters
* @returns See https://avanti.stoplight.io/docs/avanti-api/faa0ddb0eb18d-list-time-entry-templates
*/
async getTimeEntryTemplates(parameters) {
return (await this.callApi('/v1/TimeManagement/Templates', {
getParameters: parameters,
method: 'get'
}));
}
/**
* Get Report Data:
* /v1/Reporter/{reportId}
* @param reportId - The ID of the report you would like to get the data for.
* @returns See https://avanti.stoplight.io/docs/avanti-api/ed0485a9c98bb-get-report-data
*/
async getReport(reportId) {
return (await this.callApi(`/v1/Reporter/${reportId}`, {
method: 'get'
}));
}
}
export * as lookups from './lookups.js';