@tellescope/schema 0.0.7 → 0.0.11

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/src/schema.ts CHANGED
@@ -1,7 +1,33 @@
1
- import "@tellescope/types"
2
- // import "@tellescope/types-server"
1
+ import {
2
+ ServerModelForName,
3
+ DatabaseModel,
4
+ DatabaseRecord,
5
+ Enduser,
6
+ ObjectId,
7
+ ModelName,
8
+ } from "@tellescope/types-server"
9
+ import {
10
+ ErrorInfo,
11
+ Indexable,
12
+ Operation,
13
+ JSONType,
14
+ CRUD,
15
+ HTTPMethod,
16
+ SessionType,
17
+ UserIdentity,
18
+ } from "@tellescope/types-utilities"
19
+ import {
20
+ EnduserSession,
21
+ ConfiguredSession,
22
+ JourneyState,
23
+ UserSession,
24
+ AttendeeInfo,
25
+ MeetingStatus,
26
+ } from "@tellescope/types-models"
3
27
 
4
28
  import {
29
+ EscapeBuilder,
30
+
5
31
  booleanValidator,
6
32
  dateValidator,
7
33
  emailValidator,
@@ -33,130 +59,143 @@ import {
33
59
  stringValidator250,
34
60
  stringValidator5000,
35
61
  listOfDisplayNameInfo,
62
+ fileTypeValidator,
63
+ fileSizeValidator,
64
+ meetingStatusValidator,
65
+ listOfAttendeesValidator,
66
+ meetingInfoValidator,
67
+ listOfUserIndentitiesValidator,
68
+ attendeeInfoValidator,
69
+ listOfObjectAnyFieldsValidator,
70
+ meetingsListValidator,
36
71
  } from "@tellescope/validation"
37
72
 
38
73
  import {
39
74
  CREATOR_ONLY_ACCESS,
40
75
  DEFAULT_OPERATIONS,
41
76
  PLACEHOLDER_ID,
77
+ ENDUSER_SESSION_TYPE,
42
78
  } from "@tellescope/constants"
79
+ export type RelationshipConstraint<T> = {
80
+ explanation: string; // human readable, for documentation purposes
81
+ evaluate: (v: T, dependencies: Indexable<Partial<DatabaseModel>>, session: UserSession | EnduserSession) => string | void;
82
+ }
83
+
84
+ export type DependencyAccessConstraint <T> = { type: 'dependency', foreignModel: ModelName, foreignField: string, accessField: keyof T }
85
+
86
+ export type AccessConstraint <T> = { type: 'creatorOnly' }
87
+ | { type: 'filter', field: string }
88
+ | DependencyAccessConstraint<T>
89
+
90
+ export type UniqueArrayConstraint <T> = { array: keyof T, itemKey?: string }
91
+
92
+ export type Constraint <T> = {
93
+ unique: (keyof T & string | UniqueArrayConstraint<T>)[];
94
+ globalUnique?: (keyof T)[];
95
+ relationship: RelationshipConstraint<Partial<T>>[];
96
+ access?: AccessConstraint<T>[];
97
+ }
98
+
99
+ export type Initializer <T, R> = (a: T, s: UserSession | EnduserSession) => R
100
+
101
+ export type EndpointOptions = {
102
+ // parameters used for endpoint that aren't stored in the model
103
+ parameters?: { [index: string]: EscapeBuilder<any> },
104
+ }
105
+
106
+ export type DependencyDeletionAction = 'delete' | 'unset' | 'setNull' | 'nop'
107
+ export type DependecyRelationship = 'foreignKey' | 'value'
108
+
109
+ export type Dependency <T=DatabaseRecord> = {
110
+ dependsOn: ModelName[], // list of => OR, multiple dependency records => AND
111
+ dependencyField: string,
112
+ relationship: DependecyRelationship,
113
+ onDependencyDelete: DependencyDeletionAction,
114
+ getDependentValues?: (t: T) => JSONType[], // for accessing the values of a Dependency
115
+ filterByDependency?: (foreignValue: JSONType, foreignModel?: DatabaseModel) => { // for filtering against a Dependency
116
+ field: string,
117
+ value: JSONType | 'any',
118
+ },
119
+ }
120
+
121
+ export type ModelFieldInfo <T, R> = {
122
+ validator: EscapeBuilder<R>,
123
+ readonly?: boolean,
124
+ required?: boolean,
125
+ updatesDisabled?: boolean,
126
+ examples?: JSONType[],
127
+ initializer?: Initializer<Partial<T>, R>, // should include the required fields of T, not just partial
128
+ dependencies?: Dependency<Partial<T>>[],
129
+ }
130
+
131
+ export type ModelFields<T> = {
132
+ [K in keyof T]: ModelFieldInfo<T, T[K]>
133
+ }
134
+ export type extractFields<Type> = Type extends ModelFields<infer X> ? X : never
43
135
 
44
- // type RelationshipConstraint<T> = {
45
- // explanation: string; // human readable, for documentation purposes
46
- // evaluate: (v: T, dependencies: Indexable<Partial<DatabaseModel>>) => string | void;
47
- // }
48
-
49
- // type DependencyAccessConstraint <T> = { type: 'dependency', foreignModel: ModelName, foreignField: string, accessField: keyof T }
50
-
51
- // type AccessConstraint <T> = { type: 'creatorOnly' }
52
- // | { type: 'filter', field: string }
53
- // | DependencyAccessConstraint<T>
54
-
55
- // type UniqueArrayConstraint <T> = { array: keyof T, itemKey?: string }
56
-
57
- // type Constraint <T> = {
58
- // unique: (keyof T & string | UniqueArrayConstraint<T>)[];
59
- // globalUnique?: (keyof T)[];
60
- // relationship: RelationshipConstraint<Partial<T>>[];
61
- // access?: AccessConstraint<T>[];
62
- // }
63
-
64
- // type Initializer <T, R> = (a: T, s: ConfiguredSession) => R
65
-
66
- // type EndpointOptions = {
67
- // // parameters used for endpoint that aren't stored in the model
68
- // parameters?: { [index: string]: EscapeBuilder<any> },
69
- // }
70
-
71
- // type DependencyDeletionAction = 'delete' | 'unset' | 'setNull' | 'nop'
72
- // type DependecyRelationship = 'foreignKey' | 'value'
73
-
74
- // type Dependency <T=DatabaseRecord> = {
75
- // dependsOn: ModelName[], // list of => OR, multiple dependency records => AND
76
- // dependencyField: string,
77
- // relationship: DependecyRelationship,
78
- // onDependencyDelete: DependencyDeletionAction,
79
- // getDependentValues?: (t: T) => JSONType[], // for accessing the values of a Dependency
80
- // filterByDependency?: (foreignValue: JSONType, foreignModel?: DatabaseModel) => { // for filtering against a Dependency
81
- // field: string,
82
- // value: JSONType | 'any',
83
- // },
84
- // }
85
-
86
- // type ModelFieldInfo <T, R> = {
87
- // validator: EscapeBuilder<R>,
88
- // readonly?: boolean,
89
- // required?: boolean,
90
- // updatesDisabled?: boolean,
91
- // examples?: JSONType[],
92
- // initializer?: Initializer<Partial<T>, R>, // should include the required fields of T, not just partial
93
- // dependencies?: Dependency<Partial<T>>[],
94
- // }
95
-
96
- // export type ModelFields<T> = {
97
- // [K in keyof T]: ModelFieldInfo<T, T[K]>
98
- // }
99
- // type extractFields<Type> = Type extends ModelFields<infer X> ? X : never
100
-
101
- // type ArgumentInfo = {
102
- // description?: string;
103
- // }
104
-
105
- // type ActionInfo = {
106
- // name?: string,
107
- // description?: string,
108
- // notes?: string[],
109
- // warnings?: string[],
110
- // }
111
-
112
- // type CustomAction <P=any, R=any> = {
113
- // op: Operation | 'custom',
114
- // access: CRUD,
115
- // // parameters: InputValidation<P>,
116
- // parameters: ModelFields<P>,
117
- // returns: ModelFields<R>,
118
- // path?: string,
119
- // method?: HTTPMethod,
120
- // } & ActionInfo
121
-
122
- // type CustomActionsForModel = {
123
- // [K in ModelName]: { [index: string]: CustomAction }
124
- // }
125
-
126
- // type ReadFilter <T> = { [K in keyof T]?: { required: boolean } }
127
-
128
-
129
- // // m is the original model (or undefined, if create)
130
- // // allows for easier event handling based on specific updates (by comparing args to pre-update model)
131
- // type SideEffectHandler <T, O=any> = (args: Partial<T>[], m: (Partial<T> | undefined)[] | undefined, n: (Partial<T> & { _id: ObjectId })[], s: ConfiguredSession, o: O) => Promise<ErrorInfo[]>;
132
-
133
- // type SideEffect = {
134
- // name: string;
135
- // description: string;
136
- // }
137
-
138
- // export type Model<T, N extends ModelName> = {
139
- // info: {
140
- // name?: string,
141
- // description?: string,
142
- // sideEffects?: { [K in Operation]?: SideEffect[] }
143
- // },
144
- // fields: ModelFields<T>,
145
- // constraints: Constraint<T>,
146
- // defaultActions: { [K in Operation]?: ActionInfo },
147
- // customActions: CustomActionsForModel[N],
148
- // readFilter?: ReadFilter<T>,
149
- // options?: {
150
- // create?: EndpointOptions,
151
- // }
152
- // }
153
- // type extractModelType<Type> = Type extends Model<infer T, infer N> ? T : never
154
-
155
- // type DatabaseModelForName = ToServerModels<ModelForName>
156
-
157
- // type Schema = {
158
- // [N in keyof DatabaseModelForName]: Model<DatabaseModelForName[N], ModelName>
159
- // }
136
+ type ArgumentInfo = {
137
+ description?: string;
138
+ }
139
+
140
+ type ActionInfo = {
141
+ name?: string,
142
+ description?: string,
143
+ notes?: string[],
144
+ warnings?: string[],
145
+ }
146
+
147
+ type CustomAction <P=any, R=any> = {
148
+ op: Operation | 'custom',
149
+ access: CRUD,
150
+ // parameters: InputValidation<P>,
151
+ parameters: ModelFields<P>,
152
+ returns: R extends Array<any> ? ModelFieldInfo<any, R> : ModelFields<R>,
153
+ path?: string,
154
+ method?: HTTPMethod,
155
+ enduserOnly?: boolean,
156
+ } & ActionInfo
157
+
158
+ export type EnduserAction = {
159
+ field?: string,
160
+ } & ActionInfo
161
+
162
+ type CustomActionsForModel = {
163
+ [K in ModelName]: { [index: string]: CustomAction }
164
+ }
165
+
166
+ type ReadFilter <T> = { [K in keyof T]?: { required: boolean } }
167
+
168
+
169
+ // m is the original model (or undefined, if create)
170
+ // allows for easier event handling based on specific updates (by comparing args to pre-update model)
171
+ export type SideEffectHandler <T, O=any> = (args: Partial<T>[], m: (Partial<T> | undefined)[] | undefined, n: (Partial<T> & { _id: ObjectId })[], s: UserSession | EnduserSession, o: O) => Promise<ErrorInfo[]>;
172
+
173
+ type SideEffect = {
174
+ name: string;
175
+ description: string;
176
+ }
177
+
178
+ export type Model<T, N extends ModelName> = {
179
+ info: {
180
+ name?: string,
181
+ description?: string,
182
+ sideEffects?: { [K in Operation]?: SideEffect[] }
183
+ },
184
+ fields: ModelFields<T>,
185
+ constraints: Constraint<T>,
186
+ defaultActions: { [k in Operation]?: ActionInfo },
187
+ enduserActions?: { [index: string]: EnduserAction },
188
+ customActions: CustomActionsForModel[N],
189
+ readFilter?: ReadFilter<T>,
190
+ options?: {
191
+ create?: EndpointOptions,
192
+ }
193
+ }
194
+ export type extractModelType<Type> = Type extends Model<infer T, infer N> ? T : never
195
+
196
+ export type Schema = {
197
+ [N in keyof ServerModelForName]: Model<ServerModelForName[N], ModelName>
198
+ }
160
199
 
161
200
  const sideEffects = {
162
201
  trackJourneyEngagement: {
@@ -203,7 +242,11 @@ export type BuiltInFields_T = typeof BuiltInFields
203
242
  export type CustomActions = {
204
243
  api_keys: {
205
244
  create: CustomAction<{}, { id: string, key: string}>,
206
- }
245
+ },
246
+ files: {
247
+ prepare_file_upload: CustomAction<{ name: string, size: number, type: string }, { presignedUpload: object, file: File }>,
248
+ file_download_URL: CustomAction<{ secureName: string }, { downloadURL: string }>,
249
+ },
207
250
  journeys: {
208
251
  update_state: CustomAction<{ updates: JourneyState, id: string, name: string }, {}>,
209
252
  },
@@ -219,7 +262,14 @@ export type CustomActions = {
219
262
  users: {
220
263
  display_names: CustomAction<{ }, { fname: string, lname: string, id: string }[]>,
221
264
  refresh_session: CustomAction<{}, { user: UserSession, authToken: string }>,
222
- }
265
+ },
266
+ meetings: {
267
+ start_meeting: CustomAction<{ }, { id: string, meeting: object, host: AttendeeInfo }>,
268
+ end_meeting: CustomAction<{ id: string }, { }>,
269
+ add_attendees_to_meeting: CustomAction<{ id: string, attendees: UserIdentity[] }, { }>,
270
+ my_meetings: CustomAction<{}, { id: string, updatedAt: string, status: MeetingStatus }[]>
271
+ attendee_info: CustomAction<{ id: string }, { attendee: AttendeeInfo, others: UserIdentity[] }>,
272
+ },
223
273
  }
224
274
 
225
275
  export type PublicActions = {
@@ -246,72 +296,26 @@ export const schema: SchemaV1 = {
246
296
  update: [sideEffects.trackJourneyEngagement],
247
297
  }
248
298
  },
249
- customActions: {
250
- set_password: {
251
- op: "custom", access: 'update', method: "post",
252
- name: 'Set enduser password',
253
- path: '/set-enduser-password',
254
- description: "Sets (or resets) an enduser's password. Minimum length 8 characters.",
255
- parameters: {
256
- id: { validator: mongoIdStringValidator, required: true },
257
- password: { validator: stringValidator100, required: true },
258
- },
259
- returns: { } //authToken: { validator: stringValidator5000 } },
260
- },
261
- is_authenticated: {
262
- op: "custom", access: 'read', method: "get",
263
- name: 'Check enduser authentication',
264
- path: '/enduser-is-authenticated',
265
- description: "Checks the validity of an enduser's authToken",
266
- parameters: {
267
- id: { validator: mongoIdStringValidator, required: true },
268
- authToken: { validator: stringValidator5000, required: true },
269
- },
270
- returns: {
271
- isAuthenticated: { validator: booleanValidator, required: true },
272
- enduser: { validator: 'enduser' },
273
- } as any // add enduser eventually, when validator defined
274
- },
275
- refresh_session: {
276
- op: "custom", access: 'update', method: "post",
277
- name: 'Refresh enduser authentication',
278
- path: '/refresh-enduser-session',
279
- description: "When called by an authenticated enduser, generates a new session",
280
- parameters: { },
281
- enduserOnly: true,
282
- returns: {
283
- authToken: { validator: stringValidator, required: true },
284
- enduser: { validator: 'enduser' },
285
- } as any // add enduser eventually, when validator defined
286
- },
287
- logout: {
288
- op: "custom", access: 'update', method: "post",
289
- name: 'Logout enduser',
290
- path: '/logout-enduser',
291
- description: "Logs out an enduser",
292
- parameters: {},
293
- returns: {},
294
- },
299
+ constraints: {
300
+ unique: ['email', 'phone', 'externalId'],
301
+ relationship: [
302
+ {
303
+ explanation: 'One of email or phone is required',
304
+ evaluate: ({ email, phone }) => {
305
+ if (!(email || phone))
306
+ return 'One of email or phone is required'
307
+ }
308
+ }
309
+ ],
295
310
  },
311
+ defaultActions: DEFAULT_OPERATIONS,
296
312
  enduserActions: { logout: {}, refresh_session: {} },
297
- publicActions: {
298
- login: {
299
- op: "custom", access: 'read', method: "post",
300
- name: 'Login enduser',
301
- path: '/login-enduser',
302
- description: "Generates an authentication token for access to enduser-facing endpoints",
303
- enduserOnly: true, // implemented as authenticate in enduser sdk only
304
- parameters: {
305
- id: { validator: mongoIdStringValidator },
306
- password: { validator: stringValidator100, required: true }, // required until optional challenge token available
307
- phone: { validator: phoneValidator },
308
- email: { validator: emailValidator },
309
- },
310
- returns: { authToken: { validator: stringValidator5000 } },
311
- },
312
- },
313
313
  fields: {
314
314
  ...BuiltInFields,
315
+ externalId: {
316
+ validator: stringValidator250,
317
+ examples: ['addfed3e-ddea-415b-b52b-df820c944dbb'],
318
+ },
315
319
  email: {
316
320
  validator: emailValidator,
317
321
  examples: ['test@tellescope.com'],
@@ -374,23 +378,84 @@ export const schema: SchemaV1 = {
374
378
  lastCommunication: {
375
379
  validator: dateValidator,
376
380
  },
381
+ avatar: {
382
+ validator: stringValidator100,
383
+ dependencies: [
384
+ {
385
+ dependsOn: ['files'],
386
+ dependencyField: 'secureName',
387
+ relationship: 'foreignKey',
388
+ onDependencyDelete: 'unset',
389
+ },
390
+ ]
391
+ },
377
392
  // recentMessagePreview: {
378
393
  // validator: stringValidator,
379
394
  // },
380
395
  },
381
- constraints: {
382
- unique: ['email', 'phone'],
383
- relationship: [
384
- {
385
- explanation: 'One of email or phone is required',
386
- evaluate: ({ email, phone }) => {
387
- if (!(email || phone))
388
- return 'One of email or phone is required'
389
- }
390
- }
391
- ],
396
+ customActions: {
397
+ set_password: {
398
+ op: "custom", access: 'update', method: "post",
399
+ name: 'Set enduser password',
400
+ path: '/set-enduser-password',
401
+ description: "Sets (or resets) an enduser's password. Minimum length 8 characters.",
402
+ parameters: {
403
+ id: { validator: mongoIdStringValidator, required: true },
404
+ password: { validator: stringValidator100, required: true },
405
+ },
406
+ returns: { } //authToken: { validator: stringValidator5000 } },
407
+ },
408
+ is_authenticated: {
409
+ op: "custom", access: 'read', method: "get",
410
+ name: 'Check enduser authentication',
411
+ path: '/enduser-is-authenticated',
412
+ description: "Checks the validity of an enduser's authToken",
413
+ parameters: {
414
+ id: { validator: mongoIdStringValidator, required: true },
415
+ authToken: { validator: stringValidator5000, required: true },
416
+ },
417
+ returns: {
418
+ isAuthenticated: { validator: booleanValidator, required: true },
419
+ enduser: { validator: 'enduser' },
420
+ } as any // todo: add enduser eventually, when validator defined
421
+ },
422
+ refresh_session: {
423
+ op: "custom", access: 'update', method: "post",
424
+ name: 'Refresh enduser authentication',
425
+ path: '/refresh-enduser-session',
426
+ description: "When called by an authenticated enduser, generates a new session",
427
+ parameters: { },
428
+ enduserOnly: true,
429
+ returns: {
430
+ authToken: { validator: stringValidator, required: true },
431
+ enduser: { validator: 'enduser' },
432
+ } as any // todo: add enduser eventually, when validator defined
433
+ },
434
+ logout: {
435
+ op: "custom", access: 'update', method: "post",
436
+ name: 'Logout enduser',
437
+ path: '/logout-enduser',
438
+ description: "Logs out an enduser",
439
+ parameters: {},
440
+ returns: {},
441
+ },
442
+ },
443
+ publicActions: {
444
+ login: {
445
+ op: "custom", access: 'read', method: "post",
446
+ name: 'Login enduser',
447
+ path: '/login-enduser',
448
+ description: "Generates an authentication token for access to enduser-facing endpoints",
449
+ enduserOnly: true, // implemented as authenticate in enduser sdk only
450
+ parameters: {
451
+ id: { validator: mongoIdStringValidator },
452
+ password: { validator: stringValidator100, required: true }, // required until optional challenge token available
453
+ phone: { validator: phoneValidator },
454
+ email: { validator: emailValidator },
455
+ },
456
+ returns: { authToken: { validator: stringValidator5000 } },
457
+ },
392
458
  },
393
- defaultActions: DEFAULT_OPERATIONS,
394
459
  },
395
460
  api_keys: {
396
461
  info: {},
@@ -608,7 +673,7 @@ export const schema: SchemaV1 = {
608
673
  validator: mongoIdStringValidator,
609
674
  examples: [PLACEHOLDER_ID],
610
675
  readonly: true,
611
- initializer: (a, s) => (s as UserSession).userId,
676
+ initializer: (a, s) => (s as UserSession).id,
612
677
  },
613
678
  subject: {
614
679
  validator: stringValidator,
@@ -728,7 +793,7 @@ export const schema: SchemaV1 = {
728
793
  businessUserId: {
729
794
  validator: mongoIdStringValidator,
730
795
  readonly: true, // default to only self-sending, for now
731
- initializer: (a, s) => (s as UserSession).userId,
796
+ initializer: (a, s) => (s as UserSession).id,
732
797
  dependencies: [{
733
798
  dependsOn: ['users'],
734
799
  dependencyField: '_id',
@@ -764,7 +829,7 @@ export const schema: SchemaV1 = {
764
829
  chat_rooms: {
765
830
  info: {},
766
831
  constraints: {
767
- unique: [{ array: 'userIds' }],
832
+ unique: [{ array: 'userIds' }, { array: 'enduserIds' }],
768
833
  relationship: [],
769
834
  access: [
770
835
  { type: 'filter', field: 'userIds' },
@@ -805,7 +870,13 @@ export const schema: SchemaV1 = {
805
870
  validator: mongoIdStringValidator,
806
871
  initializer: () => '',
807
872
  readonly: true,
808
- }
873
+ },
874
+ ticketId: {
875
+ validator: mongoIdStringValidator,
876
+ },
877
+ endedAt: {
878
+ validator: dateValidator,
879
+ },
809
880
  },
810
881
  defaultActions: DEFAULT_OPERATIONS,
811
882
  enduserActions: { create: {}, read: {}, readMany: {} },
@@ -845,7 +916,7 @@ export const schema: SchemaV1 = {
845
916
  senderId: {
846
917
  validator: mongoIdStringValidator,
847
918
  readonly: true, // create a separate endpoint for storing enduser chats
848
- initializer: (a, s) => (s as UserSession).userId ?? (s as EnduserSession).enduserId,
919
+ initializer: (a, s) => (s as UserSession).id ?? (s as EnduserSession).id,
849
920
  examples: [PLACEHOLDER_ID],
850
921
  dependencies: [{ // can be userId or enduserId
851
922
  dependsOn: ['users', 'endusers'],
@@ -939,6 +1010,20 @@ export const schema: SchemaV1 = {
939
1010
  roles: {
940
1011
  validator: listOfStringsValidator,
941
1012
  },
1013
+ skills: {
1014
+ validator: listOfStringsValidator,
1015
+ },
1016
+ avatar: {
1017
+ validator: stringValidator100,
1018
+ dependencies: [
1019
+ {
1020
+ dependsOn: ['files'],
1021
+ dependencyField: 'secureName',
1022
+ relationship: 'foreignKey',
1023
+ onDependencyDelete: 'unset',
1024
+ },
1025
+ ]
1026
+ },
942
1027
  }
943
1028
  },
944
1029
  templates: {
@@ -971,5 +1056,207 @@ export const schema: SchemaV1 = {
971
1056
  initializer: () => 'enduser'
972
1057
  },
973
1058
  }
974
- }
1059
+ },
1060
+ files: {
1061
+ info: {},
1062
+ constraints: { unique: [], relationship: [] },
1063
+ defaultActions: { read: {}, readMany: {}, update: {} },
1064
+ fields: {
1065
+ ...BuiltInFields,
1066
+ name: {
1067
+ validator: stringValidator250,
1068
+ required: true,
1069
+ },
1070
+ size: {
1071
+ validator: fileSizeValidator,
1072
+ required: true,
1073
+ },
1074
+ type: {
1075
+ validator: fileTypeValidator,
1076
+ required: true
1077
+ },
1078
+ secureName: {
1079
+ validator: stringValidator250,
1080
+ readonly: true,
1081
+ },
1082
+ },
1083
+ enduserActions: { prepare_file_upload: {} },
1084
+ customActions: {
1085
+ prepare_file_upload: {
1086
+ op: "custom", access: 'create', method: "post",
1087
+ name: 'Prepare File Upload',
1088
+ path: '/prepare-file-upload',
1089
+ description: "Generates an upload link for a file, storing metadata as a File record.",
1090
+ parameters: {
1091
+ name: {
1092
+ validator: stringValidator250,
1093
+ required: true,
1094
+ },
1095
+ size: {
1096
+ validator: fileSizeValidator,
1097
+ required: true,
1098
+ },
1099
+ type: {
1100
+ validator: fileTypeValidator,
1101
+ required: true
1102
+ },
1103
+ },
1104
+ returns: {
1105
+ presignedUpload: {
1106
+ validator: objectAnyFieldsValidator,
1107
+ },
1108
+ file: {
1109
+ validator: 'file' as any, // todo: add file validator
1110
+ },
1111
+ },
1112
+ },
1113
+ file_download_URL: {
1114
+ op: "custom", access: 'read', method: "get",
1115
+ name: 'Generate File Download',
1116
+ path: '/file-download-link',
1117
+ description: "Generates a temporary download link for a file.",
1118
+ parameters: {
1119
+ secureName: { validator: stringValidator250 },
1120
+ },
1121
+ returns: {
1122
+ downloadURL: { validator: stringValidator250 },
1123
+ },
1124
+ },
1125
+ },
1126
+ },
1127
+ tickets: {
1128
+ info: {},
1129
+ constraints: {
1130
+ unique: [],
1131
+ relationship: [
1132
+ {
1133
+ explanation: 'When created by an enduser, enduserId must match their id',
1134
+ evaluate: ({ enduserId },_,session) => {
1135
+ if (session.type === ENDUSER_SESSION_TYPE && session.id !== enduserId)
1136
+ return "enduserId does not match creator id for enduser session"
1137
+ }
1138
+ },
1139
+ ],
1140
+ },
1141
+ defaultActions: DEFAULT_OPERATIONS,
1142
+ customActions: {},
1143
+ enduserActions: { create: {}, read: {}, readMany: {} },
1144
+ fields: {
1145
+ ...BuiltInFields,
1146
+ title: {
1147
+ validator: stringValidator100,
1148
+ required: true,
1149
+ examples: ["Ticket Name"],
1150
+ },
1151
+ enduserId: {
1152
+ validator: mongoIdStringValidator,
1153
+ required: true,
1154
+ examples: [PLACEHOLDER_ID],
1155
+ },
1156
+ closedAt: {
1157
+ validator: dateValidator,
1158
+ },
1159
+ owner: {
1160
+ validator: mongoIdStringValidator,
1161
+ },
1162
+ message: {
1163
+ validator: stringValidator5000,
1164
+ examples: ["Message"],
1165
+ },
1166
+ type: {
1167
+ validator: stringValidator100,
1168
+ },
1169
+ skillsRequired: {
1170
+ validator: listOfStringsValidator,
1171
+ },
1172
+ }
1173
+ },
1174
+ meetings: {
1175
+ info: {},
1176
+ constraints: {
1177
+ unique: [],
1178
+ relationship: [],
1179
+ },
1180
+ defaultActions: { },
1181
+ customActions: {
1182
+ start_meeting: {
1183
+ op: "custom", access: 'create', method: "post",
1184
+ name: 'Start Meeting',
1185
+ path: '/start-meeting',
1186
+ description: "Generates an video meeting room",
1187
+ parameters: { },
1188
+ returns: {
1189
+ id: {
1190
+ validator: mongoIdStringValidator,
1191
+ },
1192
+ meeting: {
1193
+ validator: objectAnyFieldsValidator,
1194
+ },
1195
+ host: {
1196
+ validator: attendeeInfoValidator,
1197
+ },
1198
+ },
1199
+ },
1200
+ end_meeting: {
1201
+ op: "custom", access: 'update', method: "post",
1202
+ name: "End Meeting",
1203
+ path: '/end-meeting',
1204
+ description: "Ends a video meeting",
1205
+ parameters: { id: { validator: mongoIdStringValidator } },
1206
+ returns: { },
1207
+ },
1208
+ add_attendees_to_meeting: {
1209
+ op: "custom", access: 'update', method: "post",
1210
+ name: 'Add Attendees to Meeting',
1211
+ path: '/add-attendees-to-meeting',
1212
+ description: "Adds other attendees to a meeting",
1213
+ parameters: {
1214
+ id: { validator: mongoIdStringValidator },
1215
+ attendees: { validator: listOfUserIndentitiesValidator },
1216
+ },
1217
+ returns: { },
1218
+ },
1219
+ attendee_info: {
1220
+ op: "custom", access: 'read', method: "get",
1221
+ name: 'Get attendee info for meeting',
1222
+ path: '/attendee-info',
1223
+ description: "Gets meeting info for the current user, and details about other attendees",
1224
+ parameters: {
1225
+ id: { validator: mongoIdStringValidator },
1226
+ },
1227
+ returns: {
1228
+ attendee: { validator: attendeeInfoValidator },
1229
+ others: { validator: listOfUserIndentitiesValidator },
1230
+ },
1231
+ },
1232
+ my_meetings: {
1233
+ op: "custom", access: 'read', method: "get",
1234
+ name: 'Get list of meetings',
1235
+ path: '/my-meetings',
1236
+ description: "Gets meetings for the current user.",
1237
+ parameters: {
1238
+ id: { validator: mongoIdStringValidator },
1239
+ },
1240
+ returns: { validator: meetingsListValidator },
1241
+ }
1242
+ },
1243
+ enduserActions: { my_meetings: {} },
1244
+ fields: {
1245
+ ...BuiltInFields,
1246
+ // all fields are updatable by custom endpoints only
1247
+ status: {
1248
+ validator: meetingStatusValidator,
1249
+ readonly: true,
1250
+ initializer: () => 'scheduled',
1251
+ },
1252
+ attendees: {
1253
+ validator: listOfAttendeesValidator,
1254
+ readonly: true,
1255
+ },
1256
+ meetingInfo: {
1257
+ validator: meetingInfoValidator,
1258
+ readonly: true
1259
+ }
1260
+ }
1261
+ },
975
1262
  }