CHI-3946-productionise_backend - #4575
Conversation
# Conflicts: # lambdas/account-scoped/src/router.ts
There was a problem hiding this comment.
Pull request overview
Adds first-pass voicemail channel support end-to-end: a new Flex UI panel for voicemail tasks, backend webhook handling to create voicemail TaskRouter tasks on recording completion, and wiring to attach voicemail recordings (S3) to newly created HRM contacts. It also standardizes channel typing by moving custom channel constants/types into @tech-matters/twilio-types and updates lambdas/tests accordingly.
Changes:
- Introduces a voicemail task panel in the Flex plugin (recording playback + callback action) and registers it in plugin setup.
- Adds an account-scoped
voicemail/recordingCompleteCallbackwebhook to create voicemail tasks and enrich attributes for downstream processing. - Expands channel type support (voicemail) across shared types, identifier sanitization, queue UI, icons/colors, and custom-channel typing.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| plugin-hrm-form/src/voicemail/VoicemailTaskPanel.tsx | New voicemail task UI panel (recording playback + callback action). |
| plugin-hrm-form/src/voicemail/setUpVoicemailComponents.tsx | Registers voicemail UI components into Flex. |
| plugin-hrm-form/src/utils/task.ts | Adds identifier normalization for voicemail channel. |
| plugin-hrm-form/src/translations/en.json | Adds UI strings for voicemail panel actions. |
| plugin-hrm-form/src/transfer/transferTaskState.ts | Adjusts conference transfer gating to use isVoiceTask. |
| plugin-hrm-form/src/states/DomainConstants.ts | Adds voicemail to channel constants. |
| plugin-hrm-form/src/HrmFormPlugin.tsx | Wires voicemail component setup into plugin initialization. |
| plugin-hrm-form/src/components/queuesStatus/QueueCard.tsx | Displays voicemail channel in queue status UI. |
| plugin-hrm-form/src/components/queuesStatus/helpers.ts | Initializes voicemail counts in queue status data model. |
| plugin-hrm-form/src/components/profile/IdentifierBanner/iconsFromTask.ts | Adds voicemail icon mapping for task identifier banner. |
| plugin-hrm-form/src/components/contact/MediaSection/RecordingSection.tsx | Adds autoLoad support and makes overlay-loader optional. |
| plugin-hrm-form/src/components/common/icons/VoicemailIcon.tsx | Adds voicemail SVG icon component. |
| plugin-hrm-form/src/components/case/timeline/TimelineIcon.tsx | Adds voicemail icon support in timeline rendering. |
| plugin-hrm-form/src/channels/colors.ts | Adds voicemail color (mapped to voice color). |
| lambdas/packages/twilio-types/src/index.ts | Adds RecordingSid type and re-exports channel type definitions. |
| lambdas/packages/twilio-types/src/channelType.ts | Introduces shared channel/custom-channel constants and type guards. |
| lambdas/packages/hrm-types/src/index.ts | Adds voicemail to HRM ChannelTypes. |
| lambdas/account-scoped/tests/unit/hrm/getProfileFlagsForIdentifier.test.ts | Updates tests to use shared channel constants/types. |
| lambdas/account-scoped/tests/unit/conversation/janitorTaskRouterListener.test.ts | Updates custom-channel mocking to use shared type guard. |
| lambdas/account-scoped/tests/service/hrm/getProfileFlagsForIdentifier.test.ts | Adjusts service test fixture channel typing. |
| lambdas/account-scoped/src/voicemail/recordingCompleteCallback.ts | New webhook handler to create voicemail tasks on recording completion. |
| lambdas/account-scoped/src/router.ts | Registers the new voicemail webhook route. |
| lambdas/account-scoped/src/hrm/sanitizeIdentifier.ts | Adds voicemail identifier sanitization and tightens channel typing. |
| lambdas/account-scoped/src/hrm/createHrmContactTaskRouterListener.ts | Attaches external voicemail recording (S3) to HRM contact on creation. |
| lambdas/account-scoped/src/customChannels/telegram/telegramToFlex.ts | Switches to shared custom-channel constants. |
| lambdas/account-scoped/src/customChannels/modica/modicaToFlex.ts | Switches to shared custom-channel constants. |
| lambdas/account-scoped/src/customChannels/line/lineToFlex.ts | Switches to shared custom-channel constants. |
| lambdas/account-scoped/src/customChannels/instagram/instagramToFlex.ts | Switches to shared custom-channel constants. |
| lambdas/account-scoped/src/customChannels/customChannelToFlex.ts | Removes re-export of custom-channel enum/guard from local module. |
| lambdas/account-scoped/src/customChannels/configuration.ts | Tightens channel typing for Studio flow SID lookup. |
| lambdas/account-scoped/src/conversation/janitorTaskRouterListener.ts | Uses shared custom-channel type guard and channel typing. |
| lambdas/account-scoped/src/conversation/getExternalRecordingS3Location.ts | Refactors to expose a reusable function used by HRM contact creation. |
| lambdas/account-scoped/src/conversation/createConversation.ts | Updates conversation creation typing to shared channel types. |
| lambdas/account-scoped/src/channelCapture/postSurveyListener.ts | Tightens channel typing to shared ChannelType. |
| lambdas/account-scoped/src/channelCapture/channelCaptureHandlers.ts | Tightens captured channel typing to shared ChannelType. |
Suppressed comments (5)
plugin-hrm-form/src/voicemail/VoicemailTaskPanel.tsx:35
taskis dereferenced in the selector (task.taskSid) before the null guard, which can throw if the task context hasn’t been provided yet. GuardtaskSidbefore callingselectContactByTaskSidso the component can safely rendernullwhentaskis missing.
lambdas/account-scoped/src/voicemail/recordingCompleteCallback.ts:39- Avoid logging the full webhook
body(it includes PII likefrom, and potentially routing attributes). Also validate required fields (from,callSid,recordingSid,voicemailWorkflowSid) up-front so malformed callbacks return a controlled 400 instead of throwing downstream.
console.debug('recordingCompleteCallback body', JSON.stringify(body, null, 2));
const { from, callSid, recordingSid, maxCallbackAttempts } =
body as RecordingCompleteCallbackRequestBody;
lambdas/account-scoped/src/voicemail/recordingCompleteCallback.ts:56
- In the nested catch block you log
recordingErrorinstead of the actualcallError, which makes troubleshooting fallback failures misleading.
recordingError,
lambdas/account-scoped/src/voicemail/recordingCompleteCallback.ts:71
JSON.parse(body.routingAttributes)can throw and fail the whole callback ifroutingAttributesis missing or malformed. Since this is webhook input, parse defensively and fall back to{}on invalid JSON.
attributes: JSON.stringify({
...(body.routingAttributes ? JSON.parse(body.routingAttributes) : {}),
receivedTime: receivedTime.toISOString(),
callbackAttemptsMade: 0,
maxCallbackAttempts: maxCallbackAttempts ?? DEFAULT_MAX_CALLBACK_ATTEMPTS,
plugin-hrm-form/src/voicemail/VoicemailTaskPanel.tsx:53
externalStoredRecordingcan beundefined, but the panel still rendersRecordingSectionwithautoLoad. With the currentRecordingSectionimplementation this can trigger an exception path when no S3 recording exists yet. Also, the current file had a placeholder “Retry later” action; until it’s implemented it’s safer to keep it out of the UI.
| import { | ||
| ChannelType, | ||
| isAseloCustomChannelType, | ||
| } from '@tech-matters/twilio-types/src/channelType'; |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…into CHI-3946-productionise_backend # Conflicts: # lambdas/account-scoped/src/voicemail/recordingCompleteCallback.ts
|
@copilot Fix the failing tests and linter issues |
Co-authored-by: stephenhand <1694716+stephenhand@users.noreply.github.com>
Fixed in the latest commit:
|
|
@copilot Rework the code that posts conversationMedia to the HRM backend service. Currently requests are initiated from the flex front end plugin, or as part of the contact creation portion of the contact creation Move all conversation media item creation to task router event handlers in the account scoped lambda. Voice recordings and transcripts media records should be created on task wrapup / completion, voicemail should continue to be executed from its current location but share common code Retain the current flex code, have a feature flag to control whether conversationMedia items are created from Flex or the task router handler Do this work in a separate PR |
|
@stephenhand I've opened a new pull request, #4578, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@copilot fix the failing lambda unit test by mocking patchTaskAttributes and expecting it to have been called with the correct attributes rather than testing for the underlying Twilio API call |
…askAttributes Co-authored-by: stephenhand <1694716+stephenhand@users.noreply.github.com>
Fixed in the latest commit. The test now mocks |
|
@copilot Add comprehensive unit test coverage for the new or extended lambda code in this PR |
Co-authored-by: stephenhand <1694716+stephenhand@users.noreply.github.com>
Added comprehensive unit tests in the latest commit: New file:
Extended:
All 44 unit test suites (402 tests) pass. |
Description
Checklist
Other Related Issues
None
Verification steps
AFTER YOU MERGE
You are responsible for ensuring the above steps are completed. If you move a ticket into QA without advising what version to test, the QA team will assume the latest tag has the changes. If it does not, the following confusion is on you! :-P