Hi Team ,
I'm following this tutorial to bulk download a number of recordings using the Javascript SDK.
https://developer.genesys.cloud/analyticsdatamanagement/recording/recordings-downloader#configure-sdk-settings
I have set up the javascript file in a nodejs environment . I want to download recordings from .ie org.
I am getting an error which I don't understand. Want your help to mitigate the error .
Below I have pasted the js file and the error .
This is the js file which I am executing :
// >> START recordings-downloader
// Import built in libraries needed.
const http = require('https');
const fs = require('fs');
let batchRequestBody = {
batchDownloadRequestList: []
};
// Set Genesys Cloud objects
const platformClient = require('purecloud-platform-client-v2');
const client = platformClient.ApiClient.instance;
// >> START recordings-downloader-step-4
// Create API instances
const conversationsApi = new platformClient.ConversationsApi();
const recordingApi = new platformClient.RecordingApi();
// >> END recordings-downloader-step-4
// >> START recordings-downloader-step-1
// Get client credentials from environment variables
const CLIENT_ID = process.env.GENESYS_CLOUD_CLIENT_ID;
const CLIENT_SECRET = process.env.GENESYS_CLOUD_CLIENT_SECRET;
const ORG_REGION = process.env.GENESYS_CLOUD_REGION; // eg. us_east_1
const CLOUD_ENVIRONMENT = process.env.GENESYS_CLOUD_ENVIRONMENT;
// >> START recordings-downloader-step-2
// Set environment
//const environment = platformClient.PureCloudRegionHosts[ORG_REGION];
const environment = CLOUD_ENVIRONMENT;
if(environment) client.setEnvironment(environment);
// >> END recordings-downloader-step-2
// >> END recordings-downloader-step-1
// >> START recordings-downloader-step-3
// OAuth input
client.loginClientCredentialsGrant(CLIENT_ID, CLIENT_SECRET)
// >> END recordings-downloader-step-3
.then(() => {
let dates = "2022-03-09T13:00:00.000Z/2022-03-10T00:00:00.000Z";
downloadAllRecordings(dates);
})
.catch((err) => {
// Handle failure response
console.log(err);
});
// >> START recordings-downloader-step-5
// Process and build the request for downloading the recordings
// Get the conversations within the date interval and start adding them to batch request
function downloadAllRecordings (dates) {
console.log('Start batch request process');
let body = {
interval: dates
}; // Object | query
conversationsApi.postAnalyticsConversationsDetailsQuery(body)
.then((conversationDetails) => {
let conversationDetail = [];
for (conversations of conversationDetails.conversations) {
conversationDetail.push(addConversationRecordingsToBatch(conversations.conversationId));
}
return Promise.all(conversationDetail);
})
// Send a batch request and start polling for updates
.then(() => {
return recordingApi.postRecordingBatchrequests(batchRequestBody);
})
// Start downloading the recording files individually
.then((result) => {
return getRecordingStatus(result);
})
.then((completedBatchStatus) => {
for (recording of completedBatchStatus.results) {
// If there is an errorMsg skip the recording download
if (recording.errorMsg) {
console.log("Skipping this recording. Reason: " + recording.errorMsg)
continue;
} else {
downloadRecording(recording);
}
}
})
.catch((err) => {
console.log('There was an error: ');
console.error(err);
});
}
// >> END recordings-downloader-step-5
// >> START recordings-downloader-step-6
// Get all the recordings metadata of the conversation and add it to the global batch request object
function addConversationRecordingsToBatch (conversationId) {
return recordingApi.getConversationRecordingmetadata(conversationId)
.then((recordingsData) => {
// Iterate through every result, check if there are one or more recordingIds in every conversation
for (recording of recordingsData) {
let batchRequest = {};
batchRequest.conversationId = recording.conversationId;
batchRequest.recordingId = recording.id;
batchRequestBody.batchDownloadRequestList.push(batchRequest);
console.log('Added ' + recording.conversationId + ' to batch request');
}
})
.catch((err) => {
console.log('There was a failure calling getConversationRecordingmetadata');
console.error(err);
});
}
// >> END recordings-downloader-step-6
// Plot conversationId and recordingId to request for batchdownload Recordings
function getRecordingStatus (recordingBatchRequest) {
return new Promise((resolve, reject) => {
let recursiveRequest = () => {
recordingApi.getRecordingBatchrequest(recordingBatchRequest.id)
.then((result) => {
if (result.expectedResultCount !== result.resultCount) {
console.log('Batch Result Status:' + result.resultCount + '/' + result.expectedResultCount)
// Simple polling through recursion
setTimeout(() => recursiveRequest(), 5000);
} else {
// Once result count reach expected.
resolve(result);
}
})
.catch((err) => {
console.log('There was a failure calling getRecordingBatchrequest');
console.error(err);
reject(err);
});
};
recursiveRequest();
});
}
// >> START recordings-downloader-step-7
// Get extension of every recording
function getExtension (recording) {
// Store the contentType to a variable that will be used later to determine the extension of recordings
let contentType = recording.contentType;
// Split the text and gets the extension that will be used for the recording
let ext = contentType.split('/').slice(-1);
ext = String(ext);
// For the JSON special case
if (ext.length >= 4) {
console.log('length' + ext.length);
ext = ext.substring(0, 4);
return ext;
} else {
return ext;
}
}
// >> END recordings-downloader-step-7
// >> START recordings-downloader-step-8
// Download Recordings
function downloadRecording (recording) {
console.log('Downloading now. Please wait...');
let ext = getExtension(recording);
let conversationId = recording.conversationId;
let recordingId = recording.recordingId;
let sourceURL = recording.resultUrl;
let targetDirectory = '.';
let fileName = conversationId + '_' + recordingId;
const file = fs.createWriteStream((targetDirectory + fileName + '.' + ext));
http.get(sourceURL, function (response) {
response.pipe(file);
});
}
This is the console error I am getting :
[AxiosError: Request failed with status code 400] {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function] },
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Basic dW5kZWZpbmVkOnVuZGVmaW5lZA==',
'User-Agent': 'axios/0.27.2',
'Content-Length': 29
},
method: 'post',
url: 'https://login.mypurecloud.com/oauth/token',
data: 'grant_type=client_credentials'
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: 'login.mypurecloud.com',
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 10,
connecting: false,
_hadError: false,
_parent: null,
_host: 'login.mypurecloud.com',
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular *1],
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: 8,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object],
[Symbol(RequestTimeout)]: undefined
},
_header: 'POST /oauth/token HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/x-www-form-urlencoded\r\n' +
'Authorization: Basic dW5kZWZpbmVkOnVuZGVmaW5lZA==\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Content-Length: 29\r\n' +
'Host: login.mypurecloud.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/oauth/token',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 400,
statusMessage: 'Bad Request',
client: [TLSSocket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: 'https://login.mypurecloud.com/oauth/token',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'login.mypurecloud.com',
protocol: 'https:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 29,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'https://login.mypurecloud.com/oauth/token',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'content-type': [Array],
authorization: [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array]
}
},
response: {
status: 400,
statusText: 'Bad Request',
headers: {
date: 'Fri, 04 Nov 2022 12:04:45 GMT',
'content-type': 'application/json',
'content-length': '99',
connection: 'close',
'inin-correlation-id': '795bee43-dfa4-4bba-687e-ad20fc42feb6',
'strict-transport-security': 'max-age=7776000',
vary: 'Accept-Encoding'
},
config: {
transitional: [Object],
adapter: [Function: httpAdapter],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'post',
url: 'https://login.mypurecloud.com/oauth/token',
data: 'grant_type=client_credentials'
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [TLSSocket],
_header: 'POST /oauth/token HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Content-Type: application/x-www-form-urlencoded\r\n' +
'Authorization: Basic dW5kZWZpbmVkOnVuZGVmaW5lZA==\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Content-Length: 29\r\n' +
'Host: login.mypurecloud.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/oauth/token',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'login.mypurecloud.com',
protocol: 'https:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: {
error: 'invalid_client',
description: 'client not found',
error_description: 'client not found'
}
}
}
Process finished with exit code 0