@memberjunction/server 2.35.0 → 2.36.0

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.
Files changed (52) hide show
  1. package/README.md +15 -1
  2. package/dist/config.d.ts +69 -1
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +11 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/generated/generated.d.ts +15 -12
  7. package/dist/generated/generated.d.ts.map +1 -1
  8. package/dist/generated/generated.js +73 -58
  9. package/dist/generated/generated.js.map +1 -1
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +41 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/resolvers/AskSkipResolver.d.ts +60 -5
  15. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  16. package/dist/resolvers/AskSkipResolver.js +587 -31
  17. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  18. package/dist/rest/EntityCRUDHandler.d.ts +29 -0
  19. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -0
  20. package/dist/rest/EntityCRUDHandler.js +197 -0
  21. package/dist/rest/EntityCRUDHandler.js.map +1 -0
  22. package/dist/rest/RESTEndpointHandler.d.ts +41 -0
  23. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -0
  24. package/dist/rest/RESTEndpointHandler.js +537 -0
  25. package/dist/rest/RESTEndpointHandler.js.map +1 -0
  26. package/dist/rest/ViewOperationsHandler.d.ts +21 -0
  27. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -0
  28. package/dist/rest/ViewOperationsHandler.js +144 -0
  29. package/dist/rest/ViewOperationsHandler.js.map +1 -0
  30. package/dist/rest/index.d.ts +5 -0
  31. package/dist/rest/index.d.ts.map +1 -0
  32. package/dist/rest/index.js +5 -0
  33. package/dist/rest/index.js.map +1 -0
  34. package/dist/rest/setupRESTEndpoints.d.ts +12 -0
  35. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -0
  36. package/dist/rest/setupRESTEndpoints.js +27 -0
  37. package/dist/rest/setupRESTEndpoints.js.map +1 -0
  38. package/dist/scheduler/LearningCycleScheduler.d.ts +44 -0
  39. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -0
  40. package/dist/scheduler/LearningCycleScheduler.js +188 -0
  41. package/dist/scheduler/LearningCycleScheduler.js.map +1 -0
  42. package/package.json +24 -26
  43. package/src/config.ts +15 -1
  44. package/src/generated/generated.ts +53 -44
  45. package/src/index.ts +56 -1
  46. package/src/resolvers/AskSkipResolver.ts +787 -51
  47. package/src/rest/EntityCRUDHandler.ts +279 -0
  48. package/src/rest/RESTEndpointHandler.ts +834 -0
  49. package/src/rest/ViewOperationsHandler.ts +207 -0
  50. package/src/rest/index.ts +4 -0
  51. package/src/rest/setupRESTEndpoints.ts +89 -0
  52. package/src/scheduler/LearningCycleScheduler.ts +312 -0
@@ -0,0 +1,279 @@
1
+ import {
2
+ BaseEntity, CompositeKey, EntityDeleteOptions, EntityPermissionType,
3
+ EntitySaveOptions, LogError, Metadata, UserInfo
4
+ } from '@memberjunction/core';
5
+
6
+ /**
7
+ * Entity CRUD Implementation Functions for REST endpoints
8
+ * These functions contain the detailed implementation for entity operations
9
+ */
10
+ export class EntityCRUDHandler {
11
+ /**
12
+ * Create a new entity
13
+ */
14
+ static async createEntity(entityName: string, data: any, user: UserInfo): Promise<{ success: boolean, entity?: any, error?: string, details?: any, validationErrors?: any[] }> {
15
+ try {
16
+ // Get entity object
17
+ const md = new Metadata();
18
+ const entity = await md.GetEntityObject(entityName, user);
19
+
20
+ // Check permissions
21
+ if (!entity.CheckPermissions(EntityPermissionType.Create, false)) {
22
+ return {
23
+ success: false,
24
+ error: `User ${user.Name} does not have permission to create ${entityName} records`
25
+ };
26
+ }
27
+
28
+ // Create new record
29
+ entity.NewRecord();
30
+
31
+ // Extract save options if provided
32
+ const options = new EntitySaveOptions();
33
+ if (data.options) {
34
+ const { IgnoreDirtyState, SkipEntityAIActions, SkipEntityActions, ReplayOnly, SkipOldValuesCheck } = data.options;
35
+ if (IgnoreDirtyState !== undefined) options.IgnoreDirtyState = !!IgnoreDirtyState;
36
+ if (SkipEntityAIActions !== undefined) options.SkipEntityAIActions = !!SkipEntityAIActions;
37
+ if (SkipEntityActions !== undefined) options.SkipEntityActions = !!SkipEntityActions;
38
+ if (ReplayOnly !== undefined) options.ReplayOnly = !!ReplayOnly;
39
+ if (SkipOldValuesCheck !== undefined) options.SkipOldValuesCheck = !!SkipOldValuesCheck;
40
+
41
+ // Remove options from data
42
+ delete data.options;
43
+ }
44
+
45
+ // Set values on the entity
46
+ for (const key in data) {
47
+ entity.Set(key, data[key]);
48
+ }
49
+
50
+ // Validate entity
51
+ const validationResult = entity.Validate();
52
+ if (!validationResult.Success) {
53
+ return {
54
+ success: false,
55
+ error: 'Validation failed',
56
+ validationErrors: validationResult.Errors
57
+ };
58
+ }
59
+
60
+ // Save entity
61
+ const saveSuccess = await entity.Save(options);
62
+
63
+ if (!saveSuccess) {
64
+ const latestResult = entity.LatestResult;
65
+ return {
66
+ success: false,
67
+ error: latestResult?.Message || 'Failed to create entity',
68
+ details: latestResult
69
+ };
70
+ }
71
+
72
+ // Get entity data for response
73
+ const entityData = await entity.GetDataObject();
74
+ return { success: true, entity: entityData };
75
+ } catch (error: any) {
76
+ LogError(error);
77
+ return { success: false, error: error?.message || 'Unknown error' };
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Read an entity by ID
83
+ */
84
+ static async getEntity(entityName: string, id: string | number, relatedEntities: string[] = null, user: UserInfo): Promise<{ success: boolean, entity?: any, error?: string }> {
85
+ try {
86
+ // Get entity object
87
+ const md = new Metadata();
88
+ const entity = await md.GetEntityObject(entityName, user);
89
+
90
+ // Check permissions
91
+ if (!entity.CheckPermissions(EntityPermissionType.Read, false)) {
92
+ return {
93
+ success: false,
94
+ error: `User ${user.Name} does not have permission to read ${entityName} records`
95
+ };
96
+ }
97
+
98
+ // Create composite key
99
+ const compositeKey = this.createCompositeKeyFromId(entity, id);
100
+
101
+ // Load entity
102
+ const loadSuccess = await entity.InnerLoad(compositeKey, relatedEntities);
103
+
104
+ if (!loadSuccess) {
105
+ return {
106
+ success: false,
107
+ error: `${entityName} with ID ${id} not found`
108
+ };
109
+ }
110
+
111
+ // Get entity data for response
112
+ const entityData = await entity.GetDataObject();
113
+ return { success: true, entity: entityData };
114
+ } catch (error: any) {
115
+ LogError(error);
116
+ return { success: false, error: error?.message || 'Unknown error' };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Update an existing entity
122
+ */
123
+ static async updateEntity(entityName: string, id: string | number, data: any, user: UserInfo): Promise<{ success: boolean, entity?: any, error?: string, details?: any, validationErrors?: any[] }> {
124
+ try {
125
+ // Get entity object
126
+ const md = new Metadata();
127
+ const entity = await md.GetEntityObject(entityName, user);
128
+
129
+ // Check permissions
130
+ if (!entity.CheckPermissions(EntityPermissionType.Update, false)) {
131
+ return {
132
+ success: false,
133
+ error: `User ${user.Name} does not have permission to update ${entityName} records`
134
+ };
135
+ }
136
+
137
+ // Create composite key
138
+ const compositeKey = this.createCompositeKeyFromId(entity, id);
139
+
140
+ // Load entity
141
+ const loadSuccess = await entity.InnerLoad(compositeKey);
142
+
143
+ if (!loadSuccess) {
144
+ return {
145
+ success: false,
146
+ error: `${entityName} with ID ${id} not found`
147
+ };
148
+ }
149
+
150
+ // Extract save options if provided
151
+ const options = new EntitySaveOptions();
152
+ if (data.options) {
153
+ const { IgnoreDirtyState, SkipEntityAIActions, SkipEntityActions, ReplayOnly, SkipOldValuesCheck } = data.options;
154
+ if (IgnoreDirtyState !== undefined) options.IgnoreDirtyState = !!IgnoreDirtyState;
155
+ if (SkipEntityAIActions !== undefined) options.SkipEntityAIActions = !!SkipEntityAIActions;
156
+ if (SkipEntityActions !== undefined) options.SkipEntityActions = !!SkipEntityActions;
157
+ if (ReplayOnly !== undefined) options.ReplayOnly = !!ReplayOnly;
158
+ if (SkipOldValuesCheck !== undefined) options.SkipOldValuesCheck = !!SkipOldValuesCheck;
159
+
160
+ // Remove options from data
161
+ delete data.options;
162
+ }
163
+
164
+ // Update entity with new values
165
+ for (const key in data) {
166
+ entity.Set(key, data[key]);
167
+ }
168
+
169
+ // Check if entity is dirty
170
+ if (!entity.Dirty && !options.IgnoreDirtyState) {
171
+ // Nothing changed, return success
172
+ const entityData = await entity.GetDataObject();
173
+ return { success: true, entity: entityData };
174
+ }
175
+
176
+ // Validate entity
177
+ const validationResult = entity.Validate();
178
+ if (!validationResult.Success) {
179
+ return {
180
+ success: false,
181
+ error: 'Validation failed',
182
+ validationErrors: validationResult.Errors
183
+ };
184
+ }
185
+
186
+ // Save entity
187
+ const saveSuccess = await entity.Save(options);
188
+
189
+ if (!saveSuccess) {
190
+ const latestResult = entity.LatestResult;
191
+ return {
192
+ success: false,
193
+ error: latestResult?.Message || 'Failed to update entity',
194
+ details: latestResult
195
+ };
196
+ }
197
+
198
+ // Get entity data for response
199
+ const entityData = await entity.GetDataObject();
200
+ return { success: true, entity: entityData };
201
+ } catch (error: any) {
202
+ LogError(error);
203
+ return { success: false, error: error?.message || 'Unknown error' };
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Delete an entity
209
+ */
210
+ static async deleteEntity(entityName: string, id: string | number, options: EntityDeleteOptions, user: UserInfo): Promise<{ success: boolean, error?: string, details?: any }> {
211
+ try {
212
+ // Get entity object
213
+ const md = new Metadata();
214
+ const entity = await md.GetEntityObject(entityName, user);
215
+
216
+ // Check permissions
217
+ if (!entity.CheckPermissions(EntityPermissionType.Delete, false)) {
218
+ return {
219
+ success: false,
220
+ error: `User ${user.Name} does not have permission to delete ${entityName} records`
221
+ };
222
+ }
223
+
224
+ // Create composite key
225
+ const compositeKey = this.createCompositeKeyFromId(entity, id);
226
+
227
+ // Load entity
228
+ const loadSuccess = await entity.InnerLoad(compositeKey);
229
+
230
+ if (!loadSuccess) {
231
+ return {
232
+ success: false,
233
+ error: `${entityName} with ID ${id} not found`
234
+ };
235
+ }
236
+
237
+ // Delete the entity
238
+ const deleteSuccess = await entity.Delete(options);
239
+
240
+ if (!deleteSuccess) {
241
+ const latestResult = entity.LatestResult;
242
+ return {
243
+ success: false,
244
+ error: latestResult?.Message || 'Failed to delete entity',
245
+ details: latestResult
246
+ };
247
+ }
248
+
249
+ return { success: true };
250
+ } catch (error: any) {
251
+ LogError(error);
252
+ return { success: false, error: error?.message || 'Unknown error' };
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Helper method to create a composite key from an ID
258
+ */
259
+ private static createCompositeKeyFromId(entity: BaseEntity, id: string | number): CompositeKey {
260
+ if (entity.EntityInfo.PrimaryKeys.length === 1) {
261
+ // Single primary key
262
+ const primaryKeyField = entity.EntityInfo.PrimaryKeys[0].Name;
263
+ const compositeKey = new CompositeKey();
264
+ const strId = id.toString();
265
+
266
+ // Use key-value pairs instead of SetValue
267
+ compositeKey.KeyValuePairs = [
268
+ { FieldName: primaryKeyField, Value: strId }
269
+ ];
270
+
271
+ return compositeKey;
272
+ } else {
273
+ // Composite primary key - this is a simplification
274
+ // In a real implementation, you would need to parse a composite ID string
275
+ // or accept an object with all primary key values
276
+ throw new Error('Composite primary keys are not supported in this simplified implementation');
277
+ }
278
+ }
279
+ }