@defra/wls-connectors-lib
Version:
A library extracting the various connectivity functions for the wildlife service
150 lines (135 loc) • 4.43 kB
JavaScript
import { ClientCredentials } from 'simple-oauth2'
import Config from './config.js'
import { hide } from './utils.js'
import {
HTTPResponseError,
httpFetch
} from './fetch-helper.js'
import db from 'debug'
import * as _cloneDeep from 'lodash.clonedeep'
const APPLICATION_JSON = 'application/json'
const DEFAULT_FETCH_SIZE = 100
const debug = db('connectors-lib:power-platform')
const { default: cloneDeep } = _cloneDeep
/*
* Access to dynamics using the OAuth2 client credentials flow.
* See https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
*/
let accessToken
export const getToken = async () => {
try {
const msg = cloneDeep(Config.powerApps)
hide(msg, 'oauth.client.id')
hide(msg, 'oauth.client.secret')
debug(`Power Platform connections: ${JSON.stringify(msg, null, 4)}`)
const { client, auth } = Config.powerApps.oauth
const { id, secret } = client
const oauthClient = new ClientCredentials({ client: { id, secret }, auth })
if (!accessToken || accessToken.expired(Config.powerApps.tokenExpireWindow || 60)) {
accessToken = await oauthClient.getToken({ scope: Config.powerApps.scope })
}
return `${accessToken.token.token_type} ${accessToken.token.access_token}`
} catch (error) {
console.log('Access Token Error', error.message)
throw new Error(`OAUTH access token error ${error.message}`)
}
}
const fetchHeaderFunction = async () => ({
Authorization: await getToken(),
'Content-Type': 'application/json',
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
Prefer: `odata.maxpagesize=${Config.powerApps.client.fetchSize || DEFAULT_FETCH_SIZE}`
})
const batchHeaderFunction = async batchId => ({
Authorization: await getToken(),
'Content-Type': `multipart/mixed;boundary=batch_${batchId}`,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
Prefer: 'return=representation'
})
const checkResponseOkElseThrowError = async responsePromise => {
const response = await responsePromise
debug(`HTTP response code: ${JSON.stringify(response.status)}`)
if (response.ok) {
if (response.status === 204) {
return null
} else {
if (response.headers.get('content-type').includes(APPLICATION_JSON)) {
return response.json()
} else {
return response.body
}
}
} else {
if (response.status === 404) {
if (response.headers.get('content-type').includes(APPLICATION_JSON)) {
return response.json()
} else {
return response.body
}
} else {
throw new HTTPResponseError(response)
}
}
}
const batchResponseFunction = async responsePromise => {
const response = await checkResponseOkElseThrowError(responsePromise)
const errorRegEx = /{"error":{"code":"(?<code>.*)","message":"(?<message>.*)"}}/g
let result = ''
for await (const chunk of response) {
result += chunk.toString()
}
// Look for an extractable error message
const m = result.match(errorRegEx)
if (m) {
console.error(`Batch request error: ${m}`)
}
return result
}
/**
* Simplified response function - for fetches from power apps
* a status 200 with a JSON payload is always expected
* @param responsePromise
* @returns {Promise<*>}
*/
const fetchResponseFunction = async responsePromise => {
const response = await responsePromise
if (response.ok) {
return response.json()
} else {
throw new HTTPResponseError(response)
}
}
export const POWERAPPS = {
/**
* Batch operations to update the Power Platform
* @param requestHandle
* @param batchRequestBody
* @returns {Promise<*|undefined>}
*/
batchRequest: async (requestHandle, batchRequestBody) =>
httpFetch(new URL(`${Config.powerApps.client.url}/$batch`).href,
'POST',
batchRequestBody,
() => batchHeaderFunction(requestHandle.batchId),
batchResponseFunction,
Config.powerApps.client.timeout),
/**
* Get operations to retrieve data from Power Platform
* @param path
* @returns {Promise<void>}
*/
fetch: async path =>
httpFetch(new URL(`${Config.powerApps.client.url}/${path}`).href,
'GET',
null,
fetchHeaderFunction,
fetchResponseFunction,
Config.powerApps.client.timeout),
getClientUrl: () => new URL(Config.powerApps.client.url).href,
/*
* Export the HTTPResponseError to catch in the caller
*/
HTTPResponseError
}