@memberjunction/server 6.2.0-edge.2 → 6.2.0-edge.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/config.d.ts +2189 -71
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +123 -3
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +11 -23
- package/dist/context.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +928 -0
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +8482 -3649
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +43 -3
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +142 -27
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +33 -1
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +115 -49
- package/dist/index.js.map +1 -1
- package/dist/postgresqlCompat.d.ts +8 -0
- package/dist/postgresqlCompat.d.ts.map +1 -0
- package/dist/postgresqlCompat.js +19 -0
- package/dist/postgresqlCompat.js.map +1 -0
- package/dist/postgresqlPoolSettings.d.ts +108 -0
- package/dist/postgresqlPoolSettings.d.ts.map +1 -0
- package/dist/postgresqlPoolSettings.js +134 -0
- package/dist/postgresqlPoolSettings.js.map +1 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts +23 -18
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +77 -167
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +7 -23
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +9 -47
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +9 -0
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts +107 -0
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -0
- package/dist/resolvers/HumanHandoffResolver.js +324 -0
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -0
- package/dist/resolvers/MeetingResolver.d.ts +69 -0
- package/dist/resolvers/MeetingResolver.d.ts.map +1 -0
- package/dist/resolvers/MeetingResolver.js +381 -0
- package/dist/resolvers/MeetingResolver.js.map +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +34 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +202 -7
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +1 -83
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +4 -206
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/adhocTimeout.d.ts +7 -0
- package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
- package/dist/resolvers/adhocTimeout.js +12 -0
- package/dist/resolvers/adhocTimeout.js.map +1 -0
- package/dist/resolvers/meetings/index.d.ts +5 -0
- package/dist/resolvers/meetings/index.d.ts.map +1 -0
- package/dist/resolvers/meetings/index.js +5 -0
- package/dist/resolvers/meetings/index.js.map +1 -0
- package/dist/resolvers/meetings/meetingHelpers.d.ts +17 -0
- package/dist/resolvers/meetings/meetingHelpers.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingHelpers.js +212 -0
- package/dist/resolvers/meetings/meetingHelpers.js.map +1 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts +12 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingMutations.js +418 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -0
- package/dist/resolvers/meetings/meetingQueries.d.ts +7 -0
- package/dist/resolvers/meetings/meetingQueries.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingQueries.js +113 -0
- package/dist/resolvers/meetings/meetingQueries.js.map +1 -0
- package/dist/resolvers/meetings/meetingTypes.d.ts +89 -0
- package/dist/resolvers/meetings/meetingTypes.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingTypes.js +345 -0
- package/dist/resolvers/meetings/meetingTypes.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +8 -5
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +8 -0
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -1
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +36 -0
- package/dist/services/WorkQueueHostService.d.ts.map +1 -0
- package/dist/services/WorkQueueHostService.js +97 -0
- package/dist/services/WorkQueueHostService.js.map +1 -0
- package/dist/services/workQueueConfig.d.ts +103 -0
- package/dist/services/workQueueConfig.d.ts.map +1 -0
- package/dist/services/workQueueConfig.js +48 -0
- package/dist/services/workQueueConfig.js.map +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +95 -94
- package/src/__tests__/AdhocQueryResolver.bugs.test.ts +14 -35
- package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
- package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
- package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +27 -72
- package/src/__tests__/HumanHandoffResolver.test.ts +233 -0
- package/src/__tests__/MeetingResolver.test.ts +374 -0
- package/src/__tests__/RealtimeBridgeResolver.test.ts +240 -2
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +96 -0
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +21 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +291 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +41 -0
- package/src/__tests__/WorkQueueHostService.test.ts +133 -0
- package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
- package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
- package/src/__tests__/translateBracketsToPG.test.ts +20 -0
- package/src/config.ts +130 -3
- package/src/context.ts +11 -24
- package/src/generated/graphql-schemas/__mj.ts +22138 -18770
- package/src/generic/ResolverBase.ts +171 -27
- package/src/generic/RunViewResolver.ts +33 -2
- package/src/index.ts +127 -50
- package/src/postgresqlCompat.ts +21 -0
- package/src/postgresqlPoolSettings.ts +213 -0
- package/src/resolvers/AdhocQueryResolver.ts +88 -188
- package/src/resolvers/EntityRecordNameResolver.ts +9 -51
- package/src/resolvers/GetDataResolver.ts +10 -0
- package/src/resolvers/HumanHandoffResolver.ts +304 -0
- package/src/resolvers/MeetingResolver.ts +438 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +227 -13
- package/src/resolvers/RealtimeClientSessionResolver.ts +6 -252
- package/src/resolvers/adhocTimeout.ts +12 -0
- package/src/resolvers/meetings/index.ts +4 -0
- package/src/resolvers/meetings/meetingHelpers.ts +307 -0
- package/src/resolvers/meetings/meetingMutations.ts +535 -0
- package/src/resolvers/meetings/meetingQueries.ts +172 -0
- package/src/resolvers/meetings/meetingTypes.ts +238 -0
- package/src/rest/ViewOperationsHandler.ts +8 -5
- package/src/services/TaskGraphActionRunner.ts +29 -1
- package/src/services/WorkQueueHostService.ts +129 -0
- package/src/services/workQueueConfig.ts +52 -0
- package/src/types.ts +5 -0
- package/dist/resolvers/adhoc-query-helpers.d.ts +0 -33
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
- package/dist/resolvers/adhoc-query-helpers.js +0 -50
- package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
- package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
- package/src/resolvers/adhoc-query-helpers.ts +0 -67
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { Resolver, Mutation, Query, Arg, Ctx } from 'type-graphql';
|
|
2
|
+
import {
|
|
3
|
+
IMetadataProvider,
|
|
4
|
+
LogError,
|
|
5
|
+
} from '@memberjunction/core';
|
|
6
|
+
import type {
|
|
7
|
+
MJMeetingEntity,
|
|
8
|
+
MJMeetingParticipantEntity,
|
|
9
|
+
MJPhoneNumberEntity,
|
|
10
|
+
MJInteractionEntity,
|
|
11
|
+
MJInteractionEventEntity,
|
|
12
|
+
} from '@memberjunction/core-entities';
|
|
13
|
+
import { AppContext } from '../types.js';
|
|
14
|
+
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
15
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
16
|
+
import { SessionManager } from '../agentSessions/SessionManager.js';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
MeetingParticipantType,
|
|
20
|
+
MeetingType,
|
|
21
|
+
MeetingResult,
|
|
22
|
+
StartMeetingResult,
|
|
23
|
+
DialInPhoneNumberType,
|
|
24
|
+
CreateMeetingInput,
|
|
25
|
+
UpdateMeetingInput,
|
|
26
|
+
RSVPMeetingInput,
|
|
27
|
+
} from './meetings/meetingTypes.js';
|
|
28
|
+
import {
|
|
29
|
+
LoadMeetingEntity,
|
|
30
|
+
LoadParticipants,
|
|
31
|
+
IsUserHostOrCoHost,
|
|
32
|
+
ResolveDefaultPhoneNumber,
|
|
33
|
+
GenerateUniqueDialInCode,
|
|
34
|
+
EnsureMeetingInteraction,
|
|
35
|
+
RecordInteractionEvent,
|
|
36
|
+
CloseMeetingInteraction,
|
|
37
|
+
SendMeetingInviteNotification,
|
|
38
|
+
NormalizeParticipantRole,
|
|
39
|
+
NormalizeRecordingPolicy,
|
|
40
|
+
MapMeetingToType,
|
|
41
|
+
MapParticipantToType,
|
|
42
|
+
} from './meetings/meetingHelpers.js';
|
|
43
|
+
import {
|
|
44
|
+
ExecuteMyMeetings,
|
|
45
|
+
ExecuteMeeting,
|
|
46
|
+
ExecuteMeetingParticipants,
|
|
47
|
+
ExecuteAvailableDialInPhoneNumbers,
|
|
48
|
+
} from './meetings/meetingQueries.js';
|
|
49
|
+
import {
|
|
50
|
+
ExecuteCreateMeeting,
|
|
51
|
+
ExecuteUpdateMeeting,
|
|
52
|
+
ExecuteStartMeeting,
|
|
53
|
+
ExecuteEndMeeting,
|
|
54
|
+
ExecuteCancelMeeting,
|
|
55
|
+
ExecuteRSVPMeeting,
|
|
56
|
+
type MeetingHelpers,
|
|
57
|
+
} from './meetings/meetingMutations.js';
|
|
58
|
+
|
|
59
|
+
// Re-export all meeting types and helpers for external consumers
|
|
60
|
+
export * from './meetings/index.js';
|
|
61
|
+
|
|
62
|
+
@Resolver()
|
|
63
|
+
export class MeetingResolver extends ResolverBase {
|
|
64
|
+
protected readonly sessionManager = new SessionManager();
|
|
65
|
+
|
|
66
|
+
// ── Queries ──────────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Lists meetings accessible to the caller (meetings where user is host or participant).
|
|
70
|
+
*/
|
|
71
|
+
@Query(() => [MeetingType])
|
|
72
|
+
async MyMeetings(
|
|
73
|
+
@Arg('status', () => String, { nullable: true }) status?: string,
|
|
74
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
75
|
+
): Promise<MeetingType[]> {
|
|
76
|
+
try {
|
|
77
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
78
|
+
if (!user) {
|
|
79
|
+
throw new Error('User is not authenticated.');
|
|
80
|
+
}
|
|
81
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
82
|
+
if (!provider) {
|
|
83
|
+
throw new Error('Database provider is not available.');
|
|
84
|
+
}
|
|
85
|
+
return await ExecuteMyMeetings(provider, user, status);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
LogError(`[MeetingResolver] MyMeetings failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets a single meeting by ID.
|
|
94
|
+
*/
|
|
95
|
+
@Query(() => MeetingType, { nullable: true })
|
|
96
|
+
async Meeting(
|
|
97
|
+
@Arg('id', () => String) id: string,
|
|
98
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
99
|
+
): Promise<MeetingType | null> {
|
|
100
|
+
try {
|
|
101
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
102
|
+
if (!user) {
|
|
103
|
+
throw new Error('User is not authenticated.');
|
|
104
|
+
}
|
|
105
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
106
|
+
if (!provider) {
|
|
107
|
+
throw new Error('Database provider is not available.');
|
|
108
|
+
}
|
|
109
|
+
return await ExecuteMeeting(provider, user, id);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
LogError(`[MeetingResolver] Meeting query failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Gets participants for a given meeting.
|
|
118
|
+
*/
|
|
119
|
+
@Query(() => [MeetingParticipantType])
|
|
120
|
+
async MeetingParticipants(
|
|
121
|
+
@Arg('meetingID', () => String) meetingID: string,
|
|
122
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
123
|
+
): Promise<MeetingParticipantType[]> {
|
|
124
|
+
try {
|
|
125
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
126
|
+
if (!user) {
|
|
127
|
+
throw new Error('User is not authenticated.');
|
|
128
|
+
}
|
|
129
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
130
|
+
if (!provider) {
|
|
131
|
+
throw new Error('Database provider is not available.');
|
|
132
|
+
}
|
|
133
|
+
return await ExecuteMeetingParticipants(provider, user, meetingID);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
LogError(`[MeetingResolver] MeetingParticipants failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Lists available active phone numbers suitable for dial-in.
|
|
142
|
+
*/
|
|
143
|
+
@Query(() => [DialInPhoneNumberType])
|
|
144
|
+
async AvailableDialInPhoneNumbers(
|
|
145
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
146
|
+
): Promise<DialInPhoneNumberType[]> {
|
|
147
|
+
try {
|
|
148
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
149
|
+
if (!user) {
|
|
150
|
+
throw new Error('User is not authenticated.');
|
|
151
|
+
}
|
|
152
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
153
|
+
if (!provider) {
|
|
154
|
+
throw new Error('Database provider is not available.');
|
|
155
|
+
}
|
|
156
|
+
return await ExecuteAvailableDialInPhoneNumbers(provider, user);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
LogError(`[MeetingResolver] AvailableDialInPhoneNumbers failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── Mutations ────────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Creates and schedules a new meeting.
|
|
167
|
+
*/
|
|
168
|
+
@Mutation(() => MeetingResult)
|
|
169
|
+
async CreateMeeting(
|
|
170
|
+
@Arg('input', () => CreateMeetingInput) input: CreateMeetingInput,
|
|
171
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
172
|
+
): Promise<MeetingResult> {
|
|
173
|
+
try {
|
|
174
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
175
|
+
if (!user) {
|
|
176
|
+
return { Success: false, ErrorMessage: 'User is not authenticated.' };
|
|
177
|
+
}
|
|
178
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
179
|
+
if (!provider) {
|
|
180
|
+
return { Success: false, ErrorMessage: 'Database provider is not available.' };
|
|
181
|
+
}
|
|
182
|
+
return await ExecuteCreateMeeting(provider, user, input, this.getHelperOverrides());
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
185
|
+
LogError(`[MeetingResolver] CreateMeeting exception: ${msg}`);
|
|
186
|
+
return { Success: false, ErrorMessage: msg };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Updates meeting details and participant list. Host or CoHost only.
|
|
192
|
+
*/
|
|
193
|
+
@Mutation(() => MeetingResult)
|
|
194
|
+
async UpdateMeeting(
|
|
195
|
+
@Arg('input', () => UpdateMeetingInput) input: UpdateMeetingInput,
|
|
196
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
197
|
+
): Promise<MeetingResult> {
|
|
198
|
+
try {
|
|
199
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
200
|
+
if (!user) {
|
|
201
|
+
return { Success: false, ErrorMessage: 'User is not authenticated.' };
|
|
202
|
+
}
|
|
203
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
204
|
+
if (!provider) {
|
|
205
|
+
return { Success: false, ErrorMessage: 'Database provider is not available.' };
|
|
206
|
+
}
|
|
207
|
+
return await ExecuteUpdateMeeting(provider, user, input, this.getHelperOverrides());
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
210
|
+
LogError(`[MeetingResolver] UpdateMeeting exception: ${msg}`);
|
|
211
|
+
return { Success: false, ErrorMessage: msg };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Starts a meeting: transitions status to 'Live', creates or connects to the
|
|
217
|
+
* meeting Interaction record, launches configured AI agents, initiates recording
|
|
218
|
+
* if configured as Automatic, and returns a LiveKit client token for the caller.
|
|
219
|
+
*/
|
|
220
|
+
@Mutation(() => StartMeetingResult)
|
|
221
|
+
async StartMeeting(
|
|
222
|
+
@Arg('meetingID', () => String) meetingID: string,
|
|
223
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
224
|
+
): Promise<StartMeetingResult> {
|
|
225
|
+
try {
|
|
226
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
227
|
+
if (!user) {
|
|
228
|
+
return { Success: false, ErrorMessage: 'User is not authenticated.' };
|
|
229
|
+
}
|
|
230
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
231
|
+
if (!provider) {
|
|
232
|
+
return { Success: false, ErrorMessage: 'Database provider is not available.' };
|
|
233
|
+
}
|
|
234
|
+
return await ExecuteStartMeeting(provider, user, meetingID, this.sessionManager, this.getHelperOverrides());
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
237
|
+
LogError(`[MeetingResolver] StartMeeting exception: ${msg}`);
|
|
238
|
+
return { Success: false, ErrorMessage: msg };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Ends a meeting: transitions status to 'Ended', stops all agents in the room,
|
|
244
|
+
* closes the Interaction record, and records lifecycle events.
|
|
245
|
+
*/
|
|
246
|
+
@Mutation(() => MeetingResult)
|
|
247
|
+
async EndMeeting(
|
|
248
|
+
@Arg('meetingID', () => String) meetingID: string,
|
|
249
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
250
|
+
): Promise<MeetingResult> {
|
|
251
|
+
try {
|
|
252
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
253
|
+
if (!user) {
|
|
254
|
+
return { Success: false, ErrorMessage: 'User is not authenticated.' };
|
|
255
|
+
}
|
|
256
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
257
|
+
if (!provider) {
|
|
258
|
+
return { Success: false, ErrorMessage: 'Database provider is not available.' };
|
|
259
|
+
}
|
|
260
|
+
return await ExecuteEndMeeting(provider, user, meetingID, this.getHelperOverrides());
|
|
261
|
+
} catch (error) {
|
|
262
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
263
|
+
LogError(`[MeetingResolver] EndMeeting exception: ${msg}`);
|
|
264
|
+
return { Success: false, ErrorMessage: msg };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Cancels a scheduled meeting.
|
|
270
|
+
*/
|
|
271
|
+
@Mutation(() => MeetingResult)
|
|
272
|
+
async CancelMeeting(
|
|
273
|
+
@Arg('meetingID', () => String) meetingID: string,
|
|
274
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
275
|
+
): Promise<MeetingResult> {
|
|
276
|
+
try {
|
|
277
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
278
|
+
if (!user) {
|
|
279
|
+
return { Success: false, ErrorMessage: 'User is not authenticated.' };
|
|
280
|
+
}
|
|
281
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
282
|
+
if (!provider) {
|
|
283
|
+
return { Success: false, ErrorMessage: 'Database provider is not available.' };
|
|
284
|
+
}
|
|
285
|
+
return await ExecuteCancelMeeting(provider, user, meetingID, this.getHelperOverrides());
|
|
286
|
+
} catch (error) {
|
|
287
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
288
|
+
LogError(`[MeetingResolver] CancelMeeting exception: ${msg}`);
|
|
289
|
+
return { Success: false, ErrorMessage: msg };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Updates RSVP invite status for the authenticated user.
|
|
295
|
+
*/
|
|
296
|
+
@Mutation(() => MeetingResult)
|
|
297
|
+
async RSVPMeeting(
|
|
298
|
+
@Arg('input', () => RSVPMeetingInput) input: RSVPMeetingInput,
|
|
299
|
+
@Ctx() context: AppContext = {} as AppContext,
|
|
300
|
+
): Promise<MeetingResult> {
|
|
301
|
+
try {
|
|
302
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
303
|
+
if (!user) {
|
|
304
|
+
return { Success: false, ErrorMessage: 'User is not authenticated.' };
|
|
305
|
+
}
|
|
306
|
+
const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
|
|
307
|
+
if (!provider) {
|
|
308
|
+
return { Success: false, ErrorMessage: 'Database provider is not available.' };
|
|
309
|
+
}
|
|
310
|
+
return await ExecuteRSVPMeeting(provider, user, input, this.getHelperOverrides());
|
|
311
|
+
} catch (error) {
|
|
312
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
313
|
+
LogError(`[MeetingResolver] RSVPMeeting exception: ${msg}`);
|
|
314
|
+
return { Success: false, ErrorMessage: msg };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ── Helper Overrides & Delegations ───────────────────────────────────────────
|
|
319
|
+
|
|
320
|
+
protected getHelperOverrides(): Partial<MeetingHelpers> {
|
|
321
|
+
return {
|
|
322
|
+
LoadMeetingEntity: (id, user, provider) => this.loadMeetingEntity(id, user, provider),
|
|
323
|
+
LoadParticipants: (meetingID, user, provider) => this.loadParticipants(meetingID, user, provider),
|
|
324
|
+
IsUserHostOrCoHost: (meeting, user, provider) => this.isUserHostOrCoHost(meeting, user, provider),
|
|
325
|
+
ResolveDefaultPhoneNumber: (user, provider) => this.resolveDefaultPhoneNumber(user, provider),
|
|
326
|
+
GenerateUniqueDialInCode: (phoneNumberID, user, provider) => this.generateUniqueDialInCode(phoneNumberID, user, provider),
|
|
327
|
+
EnsureMeetingInteraction: (meeting, user, provider) => this.ensureMeetingInteraction(meeting, user, provider),
|
|
328
|
+
RecordInteractionEvent: (interactionID, eventType, user, provider, details) =>
|
|
329
|
+
this.recordInteractionEvent(interactionID, eventType, user, provider, details),
|
|
330
|
+
CloseMeetingInteraction: (roomName, user, provider) => this.closeMeetingInteraction(roomName, user, provider),
|
|
331
|
+
SendMeetingInviteNotification: (meeting, targetUserID, contextUser, provider) =>
|
|
332
|
+
this.sendMeetingInviteNotification(meeting, targetUserID, contextUser, provider),
|
|
333
|
+
NormalizeParticipantRole: (roleInput, defaultRole) => this.normalizeParticipantRole(roleInput, defaultRole),
|
|
334
|
+
NormalizeRecordingPolicy: (policyInput) => this.normalizeRecordingPolicy(policyInput),
|
|
335
|
+
MapMeetingToType: (m, participants) => this.mapMeetingToType(m, participants),
|
|
336
|
+
MapParticipantToType: (p, userName, userEmail) => this.mapParticipantToType(p, userName, userEmail),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
protected async loadMeetingEntity(
|
|
341
|
+
id: string,
|
|
342
|
+
user: import('@memberjunction/core').UserInfo,
|
|
343
|
+
provider: IMetadataProvider,
|
|
344
|
+
): Promise<MJMeetingEntity | null> {
|
|
345
|
+
return LoadMeetingEntity(id, user, provider);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
protected async loadParticipants(
|
|
349
|
+
meetingID: string,
|
|
350
|
+
user: import('@memberjunction/core').UserInfo,
|
|
351
|
+
provider: IMetadataProvider,
|
|
352
|
+
): Promise<MeetingParticipantType[]> {
|
|
353
|
+
return LoadParticipants(meetingID, user, provider);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
protected async isUserHostOrCoHost(
|
|
357
|
+
meeting: MJMeetingEntity,
|
|
358
|
+
user: import('@memberjunction/core').UserInfo,
|
|
359
|
+
provider: IMetadataProvider,
|
|
360
|
+
): Promise<boolean> {
|
|
361
|
+
return IsUserHostOrCoHost(meeting, user, provider);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
protected async resolveDefaultPhoneNumber(
|
|
365
|
+
user: import('@memberjunction/core').UserInfo,
|
|
366
|
+
provider: IMetadataProvider,
|
|
367
|
+
): Promise<MJPhoneNumberEntity | null> {
|
|
368
|
+
return ResolveDefaultPhoneNumber(user, provider);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
protected async generateUniqueDialInCode(
|
|
372
|
+
phoneNumberID: string,
|
|
373
|
+
user: import('@memberjunction/core').UserInfo,
|
|
374
|
+
provider: IMetadataProvider,
|
|
375
|
+
): Promise<string | undefined> {
|
|
376
|
+
return GenerateUniqueDialInCode(phoneNumberID, user, provider);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
protected async ensureMeetingInteraction(
|
|
380
|
+
meeting: MJMeetingEntity,
|
|
381
|
+
user: import('@memberjunction/core').UserInfo,
|
|
382
|
+
provider: IMetadataProvider,
|
|
383
|
+
): Promise<MJInteractionEntity | null> {
|
|
384
|
+
return EnsureMeetingInteraction(meeting, user, provider);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
protected async recordInteractionEvent(
|
|
388
|
+
interactionID: string,
|
|
389
|
+
eventType: MJInteractionEventEntity['EventType'],
|
|
390
|
+
user: import('@memberjunction/core').UserInfo,
|
|
391
|
+
provider: IMetadataProvider,
|
|
392
|
+
details?: Record<string, unknown>,
|
|
393
|
+
): Promise<void> {
|
|
394
|
+
return RecordInteractionEvent(interactionID, eventType, user, provider, details);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
protected async closeMeetingInteraction(
|
|
398
|
+
roomName: string,
|
|
399
|
+
user: import('@memberjunction/core').UserInfo,
|
|
400
|
+
provider: IMetadataProvider,
|
|
401
|
+
): Promise<void> {
|
|
402
|
+
return CloseMeetingInteraction(roomName, user, provider);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
protected async sendMeetingInviteNotification(
|
|
406
|
+
meeting: MJMeetingEntity,
|
|
407
|
+
targetUserID: string,
|
|
408
|
+
contextUser: import('@memberjunction/core').UserInfo,
|
|
409
|
+
provider: IMetadataProvider,
|
|
410
|
+
): Promise<void> {
|
|
411
|
+
return SendMeetingInviteNotification(meeting, targetUserID, contextUser, provider);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
protected normalizeParticipantRole(
|
|
415
|
+
roleInput?: string,
|
|
416
|
+
defaultRole: MJMeetingParticipantEntity['Role'] = 'Attendee',
|
|
417
|
+
): MJMeetingParticipantEntity['Role'] {
|
|
418
|
+
return NormalizeParticipantRole(roleInput, defaultRole);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
protected normalizeRecordingPolicy(
|
|
422
|
+
policyInput?: string,
|
|
423
|
+
): MJMeetingEntity['RecordingPolicy'] {
|
|
424
|
+
return NormalizeRecordingPolicy(policyInput);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
protected mapMeetingToType(m: MJMeetingEntity, participants?: MeetingParticipantType[]): MeetingType {
|
|
428
|
+
return MapMeetingToType(m, participants);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
protected mapParticipantToType(
|
|
432
|
+
p: MJMeetingParticipantEntity,
|
|
433
|
+
userNameFallback?: string | null,
|
|
434
|
+
userEmailFallback?: string | null,
|
|
435
|
+
): MeetingParticipantType {
|
|
436
|
+
return MapParticipantToType(p, userNameFallback, userEmailFallback);
|
|
437
|
+
}
|
|
438
|
+
}
|