Chat-only commands reset voicemail mailbox and voice portal passcodes via telephony_config_write, with extension lookup and unit tests. Co-authored-by: Cursor <cursoragent@cursor.com>
79 lines
2.7 KiB
JavaScript
79 lines
2.7 KiB
JavaScript
// services/voicePin/resetVoicePortalPin.js
|
|
// GET passcodeRules + PUT /v1/telephony/config/locations/{id}/voicePortal passcode
|
|
|
|
import { logger } from '../../utils/logger.js';
|
|
import { generatePin } from './generatePin.js';
|
|
|
|
const LOG_SCOPE = 'resetvppin:service';
|
|
const MAX_PUT_ATTEMPTS = 5;
|
|
|
|
/**
|
|
* Reset a location's voice portal passcode.
|
|
*
|
|
* @param {string} locationId
|
|
* @param {object} [opts]
|
|
* @param {object} [opts.passcodeRules] cached rules from resolve step
|
|
* @param {object} [opts.voicePortal] cached portal snapshot from resolve step
|
|
* @param {object} [opts.webexClient] injectable client for tests
|
|
* @returns {Promise<string>} new PIN (never log this value)
|
|
*/
|
|
export async function resetVoicePortalPin(locationId, opts = {}) {
|
|
if (!locationId) {
|
|
throw new Error('locationId is required');
|
|
}
|
|
|
|
const client = opts.webexClient || (await import('../../integrations/webex/WebexClient.js')).default;
|
|
|
|
const passcodeRules = opts.passcodeRules
|
|
|| await client.request('GET', `telephony/config/locations/${locationId}/voicePortal/passcodeRules`);
|
|
|
|
let lastError = null;
|
|
for (let attempt = 0; attempt < MAX_PUT_ATTEMPTS; attempt += 1) {
|
|
const passcode = generatePin({
|
|
rules: passcodeRules,
|
|
extension: opts.voicePortal?.extension,
|
|
});
|
|
|
|
const body = {
|
|
passcode: {
|
|
newPasscode: passcode,
|
|
confirmPasscode: passcode,
|
|
},
|
|
};
|
|
|
|
try {
|
|
await client.request('PUT', `telephony/config/locations/${locationId}/voicePortal`, body);
|
|
logger(LOG_SCOPE, `Voice portal PIN reset succeeded for location ${locationId}`, 'info');
|
|
return passcode;
|
|
} catch (err) {
|
|
lastError = err;
|
|
const status = err?.response?.status;
|
|
if (status === 400 && attempt < MAX_PUT_ATTEMPTS - 1) {
|
|
logger(LOG_SCOPE, `Voice portal PIN rejected for ${locationId} (attempt ${attempt + 1}), retrying`, 'warn');
|
|
continue;
|
|
}
|
|
|
|
if (status === 400 || status === 422) {
|
|
const existing = opts.voicePortal?.raw
|
|
|| await client.request('GET', `telephony/config/locations/${locationId}/voicePortal`).catch(() => null);
|
|
if (existing) {
|
|
const merged = {
|
|
...existing,
|
|
passcode: body.passcode,
|
|
};
|
|
try {
|
|
await client.request('PUT', `telephony/config/locations/${locationId}/voicePortal`, merged);
|
|
logger(LOG_SCOPE, `Voice portal PIN reset succeeded (merged PUT) for location ${locationId}`, 'info');
|
|
return passcode;
|
|
} catch (mergeErr) {
|
|
lastError = mergeErr;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
}
|
|
|
|
throw lastError || new Error('Voice portal PIN reset failed');
|
|
}
|