@defra/flood-webchat 0.0.1-alpha.2 → 0.0.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ import querystring from 'querystring'
2
+ import axios from 'axios'
3
+ import { isWithinHours, extractTenantId } from './utils.js'
4
+
5
+ export async function authenticate ({ authorisation, accessKey, accessSecret }) {
6
+ const uri = 'https://cxone.niceincontact.com/auth/token'
7
+
8
+ const config = {
9
+ signal: AbortSignal.timeout(3000),
10
+ headers: {
11
+ Host: 'eu1.niceincontact.com',
12
+ 'Content-Type': 'application/x-www-form-urlencoded',
13
+ Authorization: authorisation
14
+ }
15
+ }
16
+
17
+ const body = querystring.stringify({
18
+ grant_type: 'password',
19
+ username: accessKey,
20
+ password: accessSecret
21
+ })
22
+ // Cache authentication and re-authenticate when needed (lasts 1 hour?)
23
+ const auth = await axios.post(uri, body, config)
24
+ return {
25
+ token: auth.data.access_token,
26
+ tokenType: auth.data.token_type,
27
+ tenantId: extractTenantId(auth.data.id_token)
28
+ }
29
+ }
30
+
31
+ export async function getHost ({ tenantId }) {
32
+ const uri = `https://cxone.niceincontact.com/.well-known/cxone-configuration?tenantId=${tenantId}`
33
+ const config = {
34
+ signal: AbortSignal.timeout(3000)
35
+ }
36
+ const api = await axios.get(uri, config)
37
+ return `api-${api.data.area}.niceincontact.com`
38
+ }
39
+
40
+ export async function getActivity ({ tokenType, token, host, skillEndpoint, maxQueueCount }) {
41
+ const config = {
42
+ signal: AbortSignal.timeout(3000),
43
+ headers: {
44
+ Host: host,
45
+ Authorization: `${tokenType} ${token}`,
46
+ 'Content-Type': 'application/x-www-form-urlencoded'
47
+ }
48
+ }
49
+ const uri = `https://${host}${skillEndpoint}`
50
+ const skill = await axios.get(uri, config)
51
+ const activity = skill.data.skillActivity[0]
52
+ return {
53
+ hasCapacity: activity.queueCount < maxQueueCount,
54
+ hasAgentsAvailable: activity.agentsAvailable >= 1
55
+ }
56
+ }
57
+
58
+ export async function getIsOpen ({ host, token, tokenType, hoursEndpoint }) {
59
+ const config = {
60
+ signal: AbortSignal.timeout(3000),
61
+ headers: {
62
+ Host: 'api-l36.niceincontact.com',
63
+ Authorization: `${tokenType} ${token}`,
64
+ 'Content-Type': 'application/x-www-form-urlencoded'
65
+ }
66
+ }
67
+
68
+ const uri = `https://${host}${hoursEndpoint}`
69
+ const hours = await axios.get(uri, config)
70
+ const days = hours.data.resultSet.hoursOfOperationProfiles[0].days
71
+ return isWithinHours(days)
72
+ }
@@ -1,3 +1,46 @@
1
- export function foo (value) {
2
- return value === 'bar'
1
+ import { authenticate, getHost, getIsOpen, getActivity } from './client.js'
2
+
3
+ /**
4
+ * Returns webchat availability
5
+ * @param options {object}
6
+ * @param options.clientId {string}
7
+ * @param options.clientSecret {string}
8
+ * @param options.accessKey {string}
9
+ * @param options.accessSecret {string}
10
+ * @param options.skillEndpoint {string}
11
+ * @param options.hoursEndpoint {string}
12
+ * @param options.maxQueueCount {string}
13
+ * @returns {Promise<{date: Date, availability: (string)}>}
14
+ */
15
+ export default async function getAvailability ({
16
+ clientId,
17
+ clientSecret,
18
+ accessKey,
19
+ accessSecret,
20
+ skillEndpoint,
21
+ hoursEndpoint,
22
+ maxQueueCount
23
+ }) {
24
+ const authorisation = 'Basic ' + Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`).toString('base64')
25
+
26
+ // Cache authentication and re-authenticate when needed (lasts 1 hour?)
27
+ const { tenantId, token, tokenType } = await authenticate({ authorisation, accessKey, accessSecret })
28
+ const host = await getHost({ tenantId })
29
+
30
+ const [{ hasCapacity, hasAgentsAvailable }, isOpen] = await Promise.all([
31
+ getActivity({ tokenType, token, host, skillEndpoint, maxQueueCount }),
32
+ getIsOpen({ token, tokenType, host, hoursEndpoint })
33
+ ])
34
+
35
+ // Hours of operation
36
+
37
+ // Availability
38
+ const isAvailable = isOpen && hasAgentsAvailable && hasCapacity
39
+ const isExistingOnly = isOpen && hasAgentsAvailable && !hasCapacity
40
+ const availability = isAvailable ? 'AVAILABLE' : isExistingOnly ? 'EXISTING' : 'UNAVAILABLE'
41
+
42
+ return {
43
+ date: new Date(),
44
+ availability
45
+ }
3
46
  }
@@ -0,0 +1,19 @@
1
+ export const extractTenantId = (token) => {
2
+ const base64Url = token.split('.')[1]
3
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
4
+ const jsonPayload = decodeURIComponent(Buffer.from(base64, 'base64').toString('ascii').split('').map(c => {
5
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
6
+ }).join(''))
7
+
8
+ return JSON.parse(jsonPayload).tenantId
9
+ }
10
+
11
+ export const isWithinHours = (days) => {
12
+ const now = new Date()
13
+ const name = now.toLocaleDateString('en-GB', { weekday: 'long' })
14
+ const day = days.find(d => d.day.toLowerCase() === name.toLowerCase())
15
+ const date = now.toLocaleDateString('en-GB').split('/')
16
+ const open = `${date[2]}-${date[1]}-${date[0]}T${day.openTime}`
17
+ const close = `${date[2]}-${date[1]}-${date[0]}T${day.closeTime}`
18
+ return now.getTime() >= Date.parse(open) && now.getTime() <= Date.parse(close)
19
+ }