Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
470 lines
16 KiB
JSON
470 lines
16 KiB
JSON
[
|
|
{
|
|
"id": "F001",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Core migration: create online_meetings table (PK (tenant, meeting_id), all columns per PRD), distributed on tenant with transaction:false Citus helper pattern."
|
|
},
|
|
{
|
|
"id": "F002",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "online_meetings status CHECK includes scheduled, ended, recording_pending, recording_ready, no_recording, cancelled, failed."
|
|
},
|
|
{
|
|
"id": "F003",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "online_meetings unique (tenant, provider, provider_meeting_id) and indexes on (tenant, interaction_id), (tenant, appointment_request_id), (tenant, status, end_time)."
|
|
},
|
|
{
|
|
"id": "F004",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Core migration: create online_meeting_artifacts child table (PK (tenant, artifact_id), artifact_type, provider_artifact_id, content_url, document_id, file_id, created_date_time), colocated with online_meetings."
|
|
},
|
|
{
|
|
"id": "F005",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "online_meeting_artifacts unique (tenant, meeting_id, artifact_type, provider_artifact_id) for idempotent upserts; index (tenant, meeting_id)."
|
|
},
|
|
{
|
|
"id": "F006",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Rollout / Migration"
|
|
],
|
|
"description": "Both new tables apply and roll back cleanly on plain Postgres (no Citus) and on Citus."
|
|
},
|
|
{
|
|
"id": "F007",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "Core migration: insert 'Online Meeting' system interaction type with icon 'video' (INSERT-only, follows add_general_interaction_type pattern)."
|
|
},
|
|
{
|
|
"id": "F008",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Types: add IOnlineMeeting and IOnlineMeetingArtifact (with artifacts[] on IOnlineMeeting), exported from @alga-psa/types barrel."
|
|
},
|
|
{
|
|
"id": "F009",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Types: add optional read-only online_meeting?: IOnlineMeeting | null to IInteraction."
|
|
},
|
|
{
|
|
"id": "F010",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "OnlineMeetingModel (session-agnostic, createTenantKnex-based): create, getById (with artifacts), getByProviderMeetingId, getByInteractionId, getByAppointmentRequestId, update, listPendingRecordings."
|
|
},
|
|
{
|
|
"id": "F011",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "OnlineMeetingModel artifact helpers: upsertArtifact (idempotent on unique key), listArtifacts(meetingId)."
|
|
},
|
|
{
|
|
"id": "F012",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "InteractionModel.addInteraction/getById accept an optional trx (default createTenantKnex), fixing the existing gap where the model opened its own connection outside the action transaction."
|
|
},
|
|
{
|
|
"id": "F013",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Shared create-interaction helper replicates the action's status defaulting, contact->client resolution, INTERACTION_LOGGED + INTERACTION_CREATED publishing, and revalidatePath; both addInteraction action and meeting paths call it."
|
|
},
|
|
{
|
|
"id": "F014",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "onlineMeetingActions: getOnlineMeetingForInteraction (withAuth)."
|
|
},
|
|
{
|
|
"id": "F015",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements",
|
|
"Functional Requirements"
|
|
],
|
|
"description": "onlineMeetingActions: refreshMeetingRecordings(meetingId) (withAuth) invoking the shared fetch handler."
|
|
},
|
|
{
|
|
"id": "F016",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "EE: change createTeamsMeeting to create a calendar-backed event (POST /users/{organizerUpn}/events, isOnlineMeeting:true, onlineMeetingProvider:teamsForBusiness); capture event id."
|
|
},
|
|
{
|
|
"id": "F017",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "EE: resolve the onlineMeeting id from the event joinUrl (filter on JoinWebUrl, URL-encoded) for later artifact calls."
|
|
},
|
|
{
|
|
"id": "F018",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Security / Permissions",
|
|
"Functional Requirements"
|
|
],
|
|
"description": "EE: appointment-approval meetings created with NO external attendees; MSP-initiated meetings MAY include attendees."
|
|
},
|
|
{
|
|
"id": "F019",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "EE: updateTeamsMeeting and deleteTeamsMeeting move to the events endpoints (PATCH/DELETE /users/{organizerUpn}/events/{eventId})."
|
|
},
|
|
{
|
|
"id": "F020",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Facade CreateTeamsMeetingResult (and EE return) extended with organizerUpn, organizerUserId (AAD object id), and eventId; persisted on online_meetings."
|
|
},
|
|
{
|
|
"id": "F021",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "EE fetchMeetingArtifacts: GET .../users/{organizerUserId}/onlineMeetings/{id}/recordings and /transcripts (collections) + transcript content (Accept: text/vtt); URL-encoded path segments; returns content URLs not clickable links."
|
|
},
|
|
{
|
|
"id": "F022",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Add fetchMeetingArtifacts to the TeamsMeetingService facade interface with a no-op binding off-enterprise; export from EE lib index."
|
|
},
|
|
{
|
|
"id": "F023",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Appointment approval path: inside the existing createdMeeting block, create the Online Meeting interaction (via trx helper) and the online_meetings row linking appointment_request_id + interaction_id."
|
|
},
|
|
{
|
|
"id": "F024",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Appointment approval path: keep writing appointment_requests.online_meeting_* columns (email/portal still read them)."
|
|
},
|
|
{
|
|
"id": "F025",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Reschedule (updateAppointmentRequestDateTime): update the calendar event via provider_event_id and keep online_meetings + interaction times in sync."
|
|
},
|
|
{
|
|
"id": "F026",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Decline/cancel paths: set online_meetings.status = 'cancelled' and stop capture/polling."
|
|
},
|
|
{
|
|
"id": "F027",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements",
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "Update/delete branch explicitly on presence of provider_event_id: new rows use the events endpoint; legacy rows (online_meeting_id only) keep standalone onlineMeeting handling or show a controlled warning."
|
|
},
|
|
{
|
|
"id": "F028",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Non-functional Requirements"
|
|
],
|
|
"description": "All Graph create/update/delete calls run OUTSIDE DB transactions (move the reschedule updateTeamsMeeting call out of withTransaction)."
|
|
},
|
|
{
|
|
"id": "F029",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Non-functional Requirements"
|
|
],
|
|
"description": "Compensation: if Graph event creation succeeds but the subsequent DB transaction fails, delete/cancel the orphaned calendar event."
|
|
},
|
|
{
|
|
"id": "F030",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Rollout / Migration"
|
|
],
|
|
"description": "No backfill: pre-existing approved appointments get no online_meetings row or interaction; existing columns/links untouched."
|
|
},
|
|
{
|
|
"id": "F031",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements",
|
|
"Security / Permissions"
|
|
],
|
|
"description": "scheduleTeamsMeeting action (withAuth + explicit hasPermission): inputs subject, start/end, organizer default, and one of contact/client and/or schedule entry; calls the facade (calendar-backed)."
|
|
},
|
|
{
|
|
"id": "F032",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "scheduleTeamsMeeting creates the Online Meeting interaction + online_meetings row via the shared helper."
|
|
},
|
|
{
|
|
"id": "F033",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "scheduleTeamsMeeting optionally creates a schedule_entries row with work_item_type 'interaction' and work_item_id = created interaction id, linked via online_meetings.schedule_entry_id."
|
|
},
|
|
{
|
|
"id": "F034",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "QuickAddInteraction: when type = Online Meeting, show a capability-gated 'Create Teams meeting' toggle with start/end; on save call scheduleTeamsMeeting."
|
|
},
|
|
{
|
|
"id": "F035",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "EntryPopup: add a 'Generate Teams meeting' option when creating a schedule entry directly, reusing the existing teamsMeetingCapability plumbing."
|
|
},
|
|
{
|
|
"id": "F036",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Calendar-sync: write the join URL into the schedule entry notes (or add a field mapping) in BOTH eventMapping.ts copies so it appears in the pushed external event."
|
|
},
|
|
{
|
|
"id": "F037",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Shared fetch handler (no withAuth/session): resolves meeting, calls fetchMeetingArtifacts, upserts one online_meeting_artifacts row per artifact."
|
|
},
|
|
{
|
|
"id": "F038",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements",
|
|
"Security / Permissions"
|
|
],
|
|
"description": "Fetch handler stores transcript content via an INTERNAL document helper (buffer/string + explicit tenant/user) \u2014 not the withAuth uploadDocument \u2014 associated to client + contact, is_client_visible=false unless expose_recordings_in_portal is on."
|
|
},
|
|
{
|
|
"id": "F039",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Fetch handler records recording artifacts with content_url; when download_recordings is on, also streams the blob into the file store and sets the artifact file_id."
|
|
},
|
|
{
|
|
"id": "F040",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements",
|
|
"Non-functional Requirements"
|
|
],
|
|
"description": "Fetch handler sets online_meetings.status (recording_ready when any artifact exists; no_recording after the recording_fetch_attempts cap) and refreshes the interaction."
|
|
},
|
|
{
|
|
"id": "F041",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Fetch handler is idempotent: re-run upserts artifacts and skips transcript document creation when an artifact with a document_id already exists for that provider_artifact_id."
|
|
},
|
|
{
|
|
"id": "F042",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes",
|
|
"Security / Permissions"
|
|
],
|
|
"description": "Recording proxy route (internal, authenticated, tenant-scoped, streaming/backpressure): resolves the artifact content_url server-side with the app token; never exposes the raw Graph URL."
|
|
},
|
|
{
|
|
"id": "F043",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "InteractionModel.getById/getForEntity join online_meetings on (tenant, interaction_id) and its artifacts, populating IInteraction.online_meeting with an artifacts array."
|
|
},
|
|
{
|
|
"id": "F044",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "InteractionDetails: 'Online Meeting' section with Join, status, 'Refresh recordings' button, and an artifacts list (transcripts -> View transcript document; recordings -> Download recording via proxy)."
|
|
},
|
|
{
|
|
"id": "F045",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "InteractionDetails uses InteractionIcon (Online Meeting -> video) and renders 'Recording pending' / 'No recording' states."
|
|
},
|
|
{
|
|
"id": "F046",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes",
|
|
"Security / Permissions"
|
|
],
|
|
"description": "Appointment/portal views (EntryPopup, AppointmentRequestDetailsPage, AppointmentsPage) show recording/transcript links next to Join, gated on expose_recordings_in_portal (default MSP-only)."
|
|
},
|
|
{
|
|
"id": "F047",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "All new interactive elements have stable kebab-case ids and all new copy uses t('...') i18n keys."
|
|
},
|
|
{
|
|
"id": "F048",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Security / Permissions",
|
|
"Data / API / Integrations"
|
|
],
|
|
"description": "EE migration: add default_meeting_organizer_object_id, download_recordings (default false), expose_recordings_in_portal (default false) to teams_integrations."
|
|
},
|
|
{
|
|
"id": "F049",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements",
|
|
"UX / UI Notes"
|
|
],
|
|
"description": "Move the organizer setting UI from AvailabilitySettings into the Teams integration settings page (UI move only; value already on teams_integrations); resolve and persist organizer AAD object id on save."
|
|
},
|
|
{
|
|
"id": "F050",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "Tenant-settings UI controls for download_recordings and expose_recordings_in_portal in the Teams integration settings page."
|
|
},
|
|
{
|
|
"id": "F051",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Security / Permissions"
|
|
],
|
|
"description": "Capability check (meetingCapabilityActions) gains a recordingsAvailable flag; UI warns when recording/transcript consent or Exchange-side mailbox scoping is missing instead of failing silently."
|
|
},
|
|
{
|
|
"id": "F052",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Security / Permissions",
|
|
"Rollout / Migration"
|
|
],
|
|
"description": "Setup docs: document Calendars.ReadWrite (application) WITH Exchange Application Access Policy/RBAC scoping to the organizer mailbox, plus the protected/metered recording & transcript permissions and Application Access Policy."
|
|
},
|
|
{
|
|
"id": "F053",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Non-functional Requirements"
|
|
],
|
|
"description": "Feature gating: online_meetings, artifacts, and the interaction type are edition-agnostic; capture, subscriptions, and jobs gated on isEnterprise; facade no-ops off-enterprise."
|
|
},
|
|
{
|
|
"id": "F054",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "[Phase 2] Create tenant-wide Graph change-notification subscriptions on getAllRecordings and getAllTranscripts; persist subscription id/expiry on teams_integrations."
|
|
},
|
|
{
|
|
"id": "F055",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "[Phase 2] Recurring renewal job (scheduleRecurringJob, isEnterprise-gated) renews/recreates expiring subscriptions, mirroring the Microsoft email webhook renewal."
|
|
},
|
|
{
|
|
"id": "F056",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "[Phase 2] Webhook route /api/teams/webhooks/recordings: echo validationToken, respond fast, enqueue a job that calls the shared fetch handler."
|
|
},
|
|
{
|
|
"id": "F057",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements"
|
|
],
|
|
"description": "[Phase 2] clientState carries tenant/subscription routing only; the specific meeting is resolved from notification resourceData/@odata.id matched to provider_meeting_id; decrypt encrypted resource data."
|
|
}
|
|
]
|