diff --git a/lib/EnvoyAPI.js b/lib/EnvoyAPI.js index a3e54d2..5d41194 100644 --- a/lib/EnvoyAPI.js +++ b/lib/EnvoyAPI.js @@ -1,4 +1,5 @@ const request = require('request-promise-native'); +const axios = require('axios'); const EnvoyResponseError = require('./EnvoyResponseError'); /** @@ -45,7 +46,11 @@ class EnvoyAPI { throw new Error('No token supplied.'); } + this.token = token; this.baseUrl = process.env.ENVOY_BASE_URL || 'https://app.envoy.com'; + this.newUrl = 'https://api.envoy.com/v1'; + + //Request library is deprecated as of 2020. Proposing that we use Axios for future requests. this.request = request.defaults({ headers: { Authorization: `Bearer ${token}`, @@ -56,6 +61,22 @@ class EnvoyAPI { json: true, baseUrl: this.baseUrl, }); + + /* + Setting ENVOY_BASE_URL in .env to point to the new API endpoint will break some methods below relying on the old route. + A temporary workaround for now is to set a new axios default with the updated endpoint. + */ + // this.axios = axios.create({ + // headers: { + // common: { + // Authorization: `Bearer ${token}`, + // 'Content-Type': 'application/vnd.api+json', + // Accept: 'application/vnd.api+json', + // 'X-Envoy-Context': JSON.stringify(xEnvoyContext) + // } + // }, + // baseURL: this.newUrl, + // }) } /** @@ -87,7 +108,7 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } - + async locations() { const body = await this.request({ @@ -96,7 +117,7 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } - + async location(locationId) { const body = await this.request({ @@ -105,11 +126,33 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } - + async company() { const body = await this.request({ - url: '/api/v2/companies', + url: '/api/v1/companies', + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Retrieve details about an organization or account. + * @returns {Promise} + */ + async companies() { + var options = { + method: 'GET', + url: this.newUrl + `/companies`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; }); return EnvoyAPI.getDataFromBody(body); @@ -142,6 +185,35 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } + + /** + * Import employee records to the Employee Directory. The file headers must be in the following order, Full Name, Email, Mobile Number, Assistants Email. + * If your csv does not contain email entries, it will override the previous employee directory in full. + * Possibly deprecated? This route doesn't seem to exist anymore. + * @param {file} file + * @param {string} api_key + * @returns + */ + async importEmployeeRecords(file, api_key) { + var options = { + 'method': 'GET', + 'url': `https://app.envoy.com/api/configuration/employee_list/${api_key}`, + 'headers': { + 'Authorization': 'Bearer ' + this.token + }, + body: file, + json: true, + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** * Fetches the employees for this location. * @@ -236,6 +308,263 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } + /** + * Fetches an entry by id. + * @param {string | number}} Entry Id + */ + async entry(entryId) { + const body = await this.request({ + method: 'GET', + url: `/a/visitors/api/v2/entries/${entryId}` + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * + * @param {*} entry Query params in addition to dates to filter by. + * entry = { + * location: { number }, + * limit: { number }, + * offset: { number }, + * start_date: { string }, + * end_date: { string } + * } + * + * start_date: '1900-01-31' (Example date String, same format for end_date) + * @returns {Promise} + */ + async getEntriesByDate(entry) { + let params = `?filter[location]=${entry.location}&page[limit]=${entry.limit}&page[offset]=${entry.offset}&start_date=${entry.start_date}&end_date=${entry.end_date}` + const body = await this.request({ + method: 'GET', + url: `/a/visitors/api/v2/entries/${params}` + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * @param {string | number} entryId + * @param {{}} entry + * @returns {Promise} + */ + async patchEntry(entryId, entry) { + const body = await this.request({ + method: 'PATCH', + url: `/a/visitors/api/v2/entries/${entry['entry-id']}`, + body: entry + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * @param {{}} entry + * @returns {Promise} + */ + async createEntry(entry) { + const body = await this.request({ + method: 'POST', + url: `/a/visitors/api/v2/entries`, + body: entry + }) + + // Currently the route returns only a status code 204 for success. For now just return object indicating success. + return { "message": "Success" }; + } + + /** + * Fetches a WorkSchedule. + * @param { number | string } workSchedule Id. + * @returns {Promise} + */ + async workSchedule(workScheduleId) { + var options = { + method: 'GET', + url: this.newUrl + `/work-schedules/${workScheduleId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true, + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Fetches a list of WorkSchedules. + * @param {{}} workSchedule object. + * @returns {Promise} + */ + async workSchedules(workSchedule) { + var options = { + method: 'GET', + url: this.newUrl + '/work-schedules', + headers: { + Authorization: 'Bearer ' + this.token + }, + body: workSchedule, + json: true, + }; + console.log(options.url); + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Create a new work schedule for an employee. + * @param {{}} workSchedule - Work object with locationId, email, expectedArrivalAt (UTC date time format) + * @returns {Promise} + */ + async createWorkSchedule(workSchedule) { + var options = { + method: 'POST', + url: this.newUrl + `/work-schedules`, + headers: { + Authorization: 'Bearer ' + this.token + }, + body: workSchedule, + json: true, + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Deletes a work schedule item. Returns prev item to be deleted on success. + * @param {{}} workSchedule object. + * @returns {Promise} + */ + async deleteWorkSchedule(workScheduleId) { + var options = { + method: 'DELETE', + url: this.newUrl + `/work-schedules/${workScheduleId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await this.workSchedule(workScheduleId); + await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + body.message = "Work Schedule has been removed."; + return body; + } + + /** + * Check in a certain work schedule item. + * @param { number | string } workSchedule Id + * @returns {Promise} + */ + async checkInWork(workScheduleId) { + var options = { + method: 'POST', + url: this.newUrl + `/work-schedules/${workScheduleId}/checkin`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + // Returns status 204 so body has no content. + return { message: 'Successfully checked in.' } + } + + /** + * Check out a certain work schedule item. + * @param { number | string } workSchedule Id + * @returns {Promise} + */ + async checkOutWork(workScheduleId) { + var options = { + method: 'POST', + url: this.newUrl + `/work-schedules/${workScheduleId}/checkout`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + // Returns status 204 so body has no content. + return { message: 'Successfully checked out.' } + } + + /** + * @param { number|string } Invite ID. + * @returns {Promise} + */ + async getInvite(inviteId) { + var options = { + method: 'GET', + url: this.newUrl + `/invites/${inviteId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + + /** + * @param { {} } invite Object containing query params. See invites API documentation for list of all possbile Query params. + * @returns {Promise} + */ + async getInvites(invite) { + var options = { + method: 'GET', + url: this.newUrl + `/invites`, + headers: { + Authorization: 'Bearer ' + this.token + }, + qs: invite, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + /** * Creates an invite. * @@ -253,6 +582,31 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } + /** + * Create an invite using the V1 API endpoint. + * + * @param {{}} invite + * @returns {Promise} + */ + async createInviteV1(invite) { + var options = { + method: 'POST', + url: this.newUrl + `/invites`, + headers: { + Authorization: 'Bearer ' + this.token + }, + body: invite, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + /** * Updates an invite. * @@ -272,6 +626,31 @@ class EnvoyAPI { return EnvoyAPI.getDataFromBody(body); } + /** + * Updates an invite using the V1 API endpoint. + * + * @param { number|string } inviteId + * @param {{}} invite + * @returns {Promise} + */ + async updateInviteV1(inviteId, invite) { + var options = { + method: 'POST', + url: this.newUrl + `/invites/${inviteId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + body: invite, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } /** * Updates an invite. @@ -294,7 +673,7 @@ class EnvoyAPI { } /** - * Removes an invite. + * Removes an invite * * @param inviteId * @returns {Promise} @@ -306,6 +685,215 @@ class EnvoyAPI { }); } + /** + * Removes an invite using the V1 API endpoint. + * + * @param { number|string } inviteId + * @returns {Promise} + */ + async removeInviteV1(inviteId) { + var options = { + method: 'DELETE', + url: this.newUrl + `/invites/${inviteId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Get a reservation by Id. + * @params { number|string } + * @returns {Promise} + */ + async reservation(resId) { + var options = { + method: 'GET', + url: this.newUrl + `/reservations/${resId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Get reservations. + * @returns {Promise} + */ + async reservations(resParams) { + var options = { + method: 'GET', + url: this.newUrl + `/reservations`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Create a reservation + * @params { {} } A reservation object containing body params. + * @returns {Promise} + */ + async createReservation(reservation) { + var options = { + method: 'POST', + url: this.newUrl + `/reservations`, + headers: { + Authorization: 'Bearer ' + this.token + }, + body: reservation, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Check in a reservation + * @params { number|string } Reservation Id. + * @returns {Promise} + */ + async checkInReservation(resId) { + var options = { + method: 'POST', + url: this.newUrl + `/reservations/${resId}/checkin`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Check out a reservation + * @params { number|string } Reservation Id. + * @returns {Promise} + */ + async checkOutReservation(resId) { + var options = { + method: 'POST', + url: this.newUrl + `/reservations/${resId}/checkout`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Cancel a reservation + * @params { number|string } Reservation Id. + * @returns {Promise} + */ + async cancelReservation(resId) { + var options = { + method: 'POST', + url: this.newUrl + `/reservations/${resId}/cancel`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Get a space by Id. + * @params { number|string } + * @returns {Promise} + */ + async space(spaceId) { + var options = { + method: 'GET', + url: this.newUrl + `/spaces/${spaceId}`, + headers: { + Authorization: 'Bearer ' + this.token + }, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + + /** + * Get all spaces with option query params to further filter results. + * @params { {} } Object containing query params. + * @returns {Promise} + */ + async spaces(space) { + var options = { + method: 'GET', + url: this.newUrl + `/spaces`, + headers: { + Authorization: 'Bearer ' + this.token + }, + qs: space, + json: true + }; + + const body = await request(options, function (error, response) { + if (error) throw new Error(error); + response.body; + }); + + return EnvoyAPI.getDataFromBody(body); + } + /** * Updates the job. * @@ -475,4 +1063,4 @@ class EnvoyAPI { } } -module.exports = EnvoyAPI; +module.exports = EnvoyAPI; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c98ae93..bec9535 100644 --- a/package-lock.json +++ b/package-lock.json @@ -199,6 +199,27 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1101,6 +1122,11 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", diff --git a/package.json b/package.json index 0514148..107ae85 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "homepage": "https://github.com/envoy/envoy-integrations-sdk-nodejs#readme", "dependencies": { + "axios": "^0.27.2", "body-parser": "^1.19.0", "dotenv": "^8.1.0", "jsonwebtoken": "^8.5.1",