@memberjunction/server 2.112.0 → 2.113.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 (250) hide show
  1. package/dist/agents/skip-agent.d.ts +4 -4
  2. package/dist/agents/skip-agent.d.ts.map +1 -1
  3. package/dist/agents/skip-agent.js +951 -808
  4. package/dist/agents/skip-agent.js.map +1 -1
  5. package/dist/agents/skip-sdk.d.ts +1 -1
  6. package/dist/agents/skip-sdk.d.ts.map +1 -1
  7. package/dist/agents/skip-sdk.js +43 -53
  8. package/dist/agents/skip-sdk.js.map +1 -1
  9. package/dist/apolloServer/index.js +1 -1
  10. package/dist/auth/AuthProviderFactory.d.ts +1 -1
  11. package/dist/auth/AuthProviderFactory.d.ts.map +1 -1
  12. package/dist/auth/AuthProviderFactory.js +3 -1
  13. package/dist/auth/AuthProviderFactory.js.map +1 -1
  14. package/dist/auth/BaseAuthProvider.d.ts +1 -1
  15. package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
  16. package/dist/auth/BaseAuthProvider.js +2 -3
  17. package/dist/auth/BaseAuthProvider.js.map +1 -1
  18. package/dist/auth/IAuthProvider.d.ts +1 -1
  19. package/dist/auth/IAuthProvider.d.ts.map +1 -1
  20. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  21. package/dist/auth/exampleNewUserSubClass.js +1 -1
  22. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  23. package/dist/auth/index.d.ts +1 -1
  24. package/dist/auth/index.d.ts.map +1 -1
  25. package/dist/auth/index.js +6 -6
  26. package/dist/auth/index.js.map +1 -1
  27. package/dist/auth/initializeProviders.js +1 -1
  28. package/dist/auth/initializeProviders.js.map +1 -1
  29. package/dist/auth/newUsers.d.ts +1 -1
  30. package/dist/auth/newUsers.d.ts.map +1 -1
  31. package/dist/auth/newUsers.js +7 -7
  32. package/dist/auth/newUsers.js.map +1 -1
  33. package/dist/auth/providers/Auth0Provider.d.ts +1 -1
  34. package/dist/auth/providers/Auth0Provider.d.ts.map +1 -1
  35. package/dist/auth/providers/Auth0Provider.js +1 -1
  36. package/dist/auth/providers/Auth0Provider.js.map +1 -1
  37. package/dist/auth/providers/CognitoProvider.d.ts +1 -1
  38. package/dist/auth/providers/CognitoProvider.d.ts.map +1 -1
  39. package/dist/auth/providers/CognitoProvider.js +6 -3
  40. package/dist/auth/providers/CognitoProvider.js.map +1 -1
  41. package/dist/auth/providers/GoogleProvider.d.ts +1 -1
  42. package/dist/auth/providers/GoogleProvider.d.ts.map +1 -1
  43. package/dist/auth/providers/GoogleProvider.js +1 -1
  44. package/dist/auth/providers/GoogleProvider.js.map +1 -1
  45. package/dist/auth/providers/MSALProvider.d.ts +1 -1
  46. package/dist/auth/providers/MSALProvider.d.ts.map +1 -1
  47. package/dist/auth/providers/MSALProvider.js +1 -1
  48. package/dist/auth/providers/MSALProvider.js.map +1 -1
  49. package/dist/auth/providers/OktaProvider.d.ts +1 -1
  50. package/dist/auth/providers/OktaProvider.d.ts.map +1 -1
  51. package/dist/auth/providers/OktaProvider.js +1 -1
  52. package/dist/auth/providers/OktaProvider.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +10 -22
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts +1 -1
  57. package/dist/context.d.ts.map +1 -1
  58. package/dist/context.js +7 -9
  59. package/dist/context.js.map +1 -1
  60. package/dist/entitySubclasses/entityPermissions.server.d.ts +1 -1
  61. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  62. package/dist/entitySubclasses/entityPermissions.server.js +1 -1
  63. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  64. package/dist/generated/generated.d.ts +788 -658
  65. package/dist/generated/generated.d.ts.map +1 -1
  66. package/dist/generated/generated.js +2050 -3054
  67. package/dist/generated/generated.js.map +1 -1
  68. package/dist/generic/KeyInputOutputTypes.d.ts +1 -1
  69. package/dist/generic/KeyInputOutputTypes.d.ts.map +1 -1
  70. package/dist/generic/KeyInputOutputTypes.js +1 -1
  71. package/dist/generic/KeyInputOutputTypes.js.map +1 -1
  72. package/dist/generic/ResolverBase.d.ts +1 -1
  73. package/dist/generic/ResolverBase.d.ts.map +1 -1
  74. package/dist/generic/ResolverBase.js +10 -15
  75. package/dist/generic/ResolverBase.js.map +1 -1
  76. package/dist/generic/RunViewResolver.d.ts +1 -1
  77. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  78. package/dist/generic/RunViewResolver.js +15 -15
  79. package/dist/generic/RunViewResolver.js.map +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +13 -18
  82. package/dist/index.js.map +1 -1
  83. package/dist/resolvers/ActionResolver.d.ts +2 -2
  84. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  85. package/dist/resolvers/ActionResolver.js +30 -28
  86. package/dist/resolvers/ActionResolver.js.map +1 -1
  87. package/dist/resolvers/AskSkipResolver.d.ts +2 -2
  88. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  89. package/dist/resolvers/AskSkipResolver.js +50 -60
  90. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  91. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  92. package/dist/resolvers/ComponentRegistryResolver.js +38 -36
  93. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  94. package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
  95. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  96. package/dist/resolvers/CreateQueryResolver.js +40 -43
  97. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  98. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  99. package/dist/resolvers/DatasetResolver.js +1 -1
  100. package/dist/resolvers/DatasetResolver.js.map +1 -1
  101. package/dist/resolvers/EntityRecordNameResolver.d.ts +1 -1
  102. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  103. package/dist/resolvers/EntityRecordNameResolver.js +1 -1
  104. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  105. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  106. package/dist/resolvers/EntityResolver.js +1 -1
  107. package/dist/resolvers/EntityResolver.js.map +1 -1
  108. package/dist/resolvers/FileCategoryResolver.js +1 -1
  109. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  110. package/dist/resolvers/FileResolver.js +1 -1
  111. package/dist/resolvers/FileResolver.js.map +1 -1
  112. package/dist/resolvers/GetDataContextDataResolver.d.ts +1 -1
  113. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  114. package/dist/resolvers/GetDataContextDataResolver.js +5 -5
  115. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  116. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  117. package/dist/resolvers/GetDataResolver.js +6 -8
  118. package/dist/resolvers/GetDataResolver.js.map +1 -1
  119. package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
  120. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  121. package/dist/resolvers/MergeRecordsResolver.js +3 -3
  122. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  123. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
  124. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
  125. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  126. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  127. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  128. package/dist/resolvers/QueryResolver.js +11 -11
  129. package/dist/resolvers/QueryResolver.js.map +1 -1
  130. package/dist/resolvers/ReportResolver.js +1 -1
  131. package/dist/resolvers/ReportResolver.js.map +1 -1
  132. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  133. package/dist/resolvers/RunAIAgentResolver.js +28 -27
  134. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  135. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  136. package/dist/resolvers/RunAIPromptResolver.js +31 -31
  137. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  138. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  139. package/dist/resolvers/RunTemplateResolver.js +9 -9
  140. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  141. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  142. package/dist/resolvers/SqlLoggingConfigResolver.js +10 -10
  143. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  144. package/dist/resolvers/SyncDataResolver.d.ts +1 -1
  145. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  146. package/dist/resolvers/SyncDataResolver.js +14 -15
  147. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  148. package/dist/resolvers/SyncRolesUsersResolver.d.ts +1 -1
  149. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  150. package/dist/resolvers/SyncRolesUsersResolver.js +44 -48
  151. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  152. package/dist/resolvers/TaskResolver.d.ts.map +1 -1
  153. package/dist/resolvers/TaskResolver.js +7 -7
  154. package/dist/resolvers/TaskResolver.js.map +1 -1
  155. package/dist/resolvers/TransactionGroupResolver.d.ts +1 -1
  156. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  157. package/dist/resolvers/TransactionGroupResolver.js +12 -12
  158. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  159. package/dist/resolvers/UserFavoriteResolver.d.ts +1 -1
  160. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  161. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  162. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  163. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  164. package/dist/resolvers/UserViewResolver.js.map +1 -1
  165. package/dist/rest/EntityCRUDHandler.d.ts +1 -1
  166. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  167. package/dist/rest/EntityCRUDHandler.js +16 -14
  168. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  169. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  170. package/dist/rest/RESTEndpointHandler.js +25 -23
  171. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  172. package/dist/rest/ViewOperationsHandler.d.ts +1 -1
  173. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  174. package/dist/rest/ViewOperationsHandler.js +21 -17
  175. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  176. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  177. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  178. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  179. package/dist/services/ScheduledJobsService.js +6 -4
  180. package/dist/services/ScheduledJobsService.js.map +1 -1
  181. package/dist/services/TaskOrchestrator.d.ts +1 -1
  182. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  183. package/dist/services/TaskOrchestrator.js +30 -30
  184. package/dist/services/TaskOrchestrator.js.map +1 -1
  185. package/dist/types.d.ts +3 -3
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.js +1 -0
  188. package/dist/types.js.map +1 -1
  189. package/dist/util.d.ts +1 -1
  190. package/dist/util.d.ts.map +1 -1
  191. package/dist/util.js +2 -2
  192. package/dist/util.js.map +1 -1
  193. package/package.json +39 -36
  194. package/src/agents/skip-agent.ts +1200 -1067
  195. package/src/agents/skip-sdk.ts +851 -877
  196. package/src/apolloServer/index.ts +2 -2
  197. package/src/auth/AuthProviderFactory.ts +14 -8
  198. package/src/auth/BaseAuthProvider.ts +4 -5
  199. package/src/auth/IAuthProvider.ts +2 -2
  200. package/src/auth/exampleNewUserSubClass.ts +2 -9
  201. package/src/auth/index.ts +26 -31
  202. package/src/auth/initializeProviders.ts +3 -3
  203. package/src/auth/newUsers.ts +134 -166
  204. package/src/auth/providers/Auth0Provider.ts +5 -5
  205. package/src/auth/providers/CognitoProvider.ts +10 -7
  206. package/src/auth/providers/GoogleProvider.ts +5 -4
  207. package/src/auth/providers/MSALProvider.ts +5 -5
  208. package/src/auth/providers/OktaProvider.ts +7 -6
  209. package/src/config.ts +54 -63
  210. package/src/context.ts +30 -42
  211. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  212. package/src/generated/generated.ts +40442 -48106
  213. package/src/generic/KeyInputOutputTypes.ts +6 -3
  214. package/src/generic/ResolverBase.ts +78 -119
  215. package/src/generic/RunViewResolver.ts +23 -27
  216. package/src/index.ts +48 -66
  217. package/src/resolvers/ActionResolver.ts +57 -46
  218. package/src/resolvers/AskSkipResolver.ts +533 -607
  219. package/src/resolvers/ComponentRegistryResolver.ts +562 -547
  220. package/src/resolvers/CreateQueryResolver.ts +655 -683
  221. package/src/resolvers/DatasetResolver.ts +6 -5
  222. package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
  223. package/src/resolvers/EntityRecordNameResolver.ts +5 -9
  224. package/src/resolvers/EntityResolver.ts +7 -9
  225. package/src/resolvers/FileCategoryResolver.ts +2 -2
  226. package/src/resolvers/FileResolver.ts +4 -4
  227. package/src/resolvers/GetDataContextDataResolver.ts +118 -106
  228. package/src/resolvers/GetDataResolver.ts +205 -194
  229. package/src/resolvers/MergeRecordsResolver.ts +5 -5
  230. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  231. package/src/resolvers/QueryResolver.ts +78 -95
  232. package/src/resolvers/ReportResolver.ts +2 -2
  233. package/src/resolvers/RunAIAgentResolver.ts +828 -818
  234. package/src/resolvers/RunAIPromptResolver.ts +709 -693
  235. package/src/resolvers/RunTemplateResolver.ts +103 -105
  236. package/src/resolvers/SqlLoggingConfigResolver.ts +72 -69
  237. package/src/resolvers/SyncDataResolver.ts +352 -386
  238. package/src/resolvers/SyncRolesUsersResolver.ts +350 -387
  239. package/src/resolvers/TaskResolver.ts +115 -110
  240. package/src/resolvers/TransactionGroupResolver.ts +138 -143
  241. package/src/resolvers/UserFavoriteResolver.ts +8 -17
  242. package/src/resolvers/UserViewResolver.ts +12 -17
  243. package/src/rest/EntityCRUDHandler.ts +268 -291
  244. package/src/rest/RESTEndpointHandler.ts +776 -782
  245. package/src/rest/ViewOperationsHandler.ts +195 -191
  246. package/src/scheduler/LearningCycleScheduler.ts +52 -8
  247. package/src/services/ScheduledJobsService.ts +132 -129
  248. package/src/services/TaskOrchestrator.ts +776 -792
  249. package/src/types.ts +9 -15
  250. package/src/util.ts +109 -112
@@ -1,16 +1,9 @@
1
1
  import express from 'express';
2
- import {
3
- BaseEntity,
4
- CompositeKey,
5
- EntityDeleteOptions,
6
- EntityInfo,
7
- EntityPermissionType,
8
- EntitySaveOptions,
9
- LogError,
10
- Metadata,
11
- RunView,
12
- RunViewParams,
13
- } from '@memberjunction/global';
2
+ import {
3
+ BaseEntity, CompositeKey, EntityDeleteOptions, EntityInfo,
4
+ EntityPermissionType, EntitySaveOptions, LogError, Metadata,
5
+ RunView, RunViewParams
6
+ } from '@memberjunction/core';
14
7
  import { EntityCRUDHandler } from './EntityCRUDHandler.js';
15
8
  import { ViewOperationsHandler } from './ViewOperationsHandler.js';
16
9
 
@@ -18,33 +11,33 @@ import { ViewOperationsHandler } from './ViewOperationsHandler.js';
18
11
  * Configuration options for RESTEndpointHandler
19
12
  */
20
13
  export interface RESTEndpointHandlerOptions {
21
- /**
22
- * Array of entity names to include in the API (case-insensitive)
23
- * If provided, only these entities will be accessible through the REST API
24
- * Supports wildcards using '*' (e.g., 'User*' matches 'User', 'UserRole', etc.)
25
- */
26
- includeEntities?: string[];
27
-
28
- /**
29
- * Array of entity names to exclude from the API (case-insensitive)
30
- * These entities will not be accessible through the REST API
31
- * Supports wildcards using '*' (e.g., 'Secret*' matches 'Secret', 'SecretKey', etc.)
32
- * Note: Exclude patterns always override include patterns
33
- */
34
- excludeEntities?: string[];
35
-
36
- /**
37
- * Array of schema names to include in the API (case-insensitive)
38
- * If provided, only entities in these schemas will be accessible through the REST API
39
- */
40
- includeSchemas?: string[];
41
-
42
- /**
43
- * Array of schema names to exclude from the API (case-insensitive)
44
- * Entities in these schemas will not be accessible through the REST API
45
- * Note: Exclude patterns always override include patterns
46
- */
47
- excludeSchemas?: string[];
14
+ /**
15
+ * Array of entity names to include in the API (case-insensitive)
16
+ * If provided, only these entities will be accessible through the REST API
17
+ * Supports wildcards using '*' (e.g., 'User*' matches 'User', 'UserRole', etc.)
18
+ */
19
+ includeEntities?: string[];
20
+
21
+ /**
22
+ * Array of entity names to exclude from the API (case-insensitive)
23
+ * These entities will not be accessible through the REST API
24
+ * Supports wildcards using '*' (e.g., 'Secret*' matches 'Secret', 'SecretKey', etc.)
25
+ * Note: Exclude patterns always override include patterns
26
+ */
27
+ excludeEntities?: string[];
28
+
29
+ /**
30
+ * Array of schema names to include in the API (case-insensitive)
31
+ * If provided, only entities in these schemas will be accessible through the REST API
32
+ */
33
+ includeSchemas?: string[];
34
+
35
+ /**
36
+ * Array of schema names to exclude from the API (case-insensitive)
37
+ * Entities in these schemas will not be accessible through the REST API
38
+ * Note: Exclude patterns always override include patterns
39
+ */
40
+ excludeSchemas?: string[];
48
41
  }
49
42
 
50
43
  /**
@@ -53,788 +46,789 @@ export interface RESTEndpointHandlerOptions {
53
46
  * entity operations via REST instead of GraphQL
54
47
  */
55
48
  export class RESTEndpointHandler {
56
- private router: express.Router;
57
- private options: RESTEndpointHandlerOptions;
58
-
59
- constructor(options: RESTEndpointHandlerOptions = {}) {
60
- this.router = express.Router();
61
- this.options = options;
62
- this.setupRoutes();
63
- }
64
-
65
- /**
66
- * Determines if an entity is allowed based on include/exclude lists
67
- * with support for wildcards and schema-level filtering
68
- * @param entityName The name of the entity to check
69
- * @returns True if the entity is allowed, false otherwise
70
- */
71
- private isEntityAllowed(entityName: string): boolean {
72
- const name = entityName.toLowerCase();
73
- const md = new Metadata();
74
- const entity = md.Entities.find((e) => e.Name.toLowerCase() === name);
75
-
76
- // If entity not found in metadata, don't allow it
77
- if (!entity) {
78
- return false;
79
- }
80
-
81
- const schemaName = entity.SchemaName.toLowerCase();
49
+ private router: express.Router;
50
+ private options: RESTEndpointHandlerOptions;
82
51
 
83
- // 1. Check schema exclusions first (these take highest precedence)
84
- if (this.options.excludeSchemas && this.options.excludeSchemas.length > 0) {
85
- if (this.options.excludeSchemas.some((schema) => schema.toLowerCase() === schemaName)) {
86
- return false;
87
- }
52
+ constructor(options: RESTEndpointHandlerOptions = {}) {
53
+ this.router = express.Router();
54
+ this.options = options;
55
+ this.setupRoutes();
88
56
  }
89
-
90
- // 2. Check entity exclusions next (these override entity inclusions)
91
- if (this.options.excludeEntities && this.options.excludeEntities.length > 0) {
92
- // Check for direct match
93
- if (this.options.excludeEntities.includes(name)) {
94
- return false;
95
- }
96
-
97
- // Check for wildcard matches
98
- for (const pattern of this.options.excludeEntities) {
99
- if (pattern.includes('*')) {
100
- const regex = new RegExp('^' + pattern.toLowerCase().replace(/\*/g, '.*') + '$');
101
- if (regex.test(name)) {
57
+
58
+ /**
59
+ * Determines if an entity is allowed based on include/exclude lists
60
+ * with support for wildcards and schema-level filtering
61
+ * @param entityName The name of the entity to check
62
+ * @returns True if the entity is allowed, false otherwise
63
+ */
64
+ private isEntityAllowed(entityName: string): boolean {
65
+ const name = entityName.toLowerCase();
66
+ const md = new Metadata();
67
+ const entity = md.Entities.find(e => e.Name.toLowerCase() === name);
68
+
69
+ // If entity not found in metadata, don't allow it
70
+ if (!entity) {
71
+ return false;
72
+ }
73
+
74
+ const schemaName = entity.SchemaName.toLowerCase();
75
+
76
+ // 1. Check schema exclusions first (these take highest precedence)
77
+ if (this.options.excludeSchemas && this.options.excludeSchemas.length > 0) {
78
+ if (this.options.excludeSchemas.some(schema => schema.toLowerCase() === schemaName)) {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ // 2. Check entity exclusions next (these override entity inclusions)
84
+ if (this.options.excludeEntities && this.options.excludeEntities.length > 0) {
85
+ // Check for direct match
86
+ if (this.options.excludeEntities.includes(name)) {
87
+ return false;
88
+ }
89
+
90
+ // Check for wildcard matches
91
+ for (const pattern of this.options.excludeEntities) {
92
+ if (pattern.includes('*')) {
93
+ const regex = new RegExp('^' + pattern.toLowerCase().replace(/\*/g, '.*') + '$');
94
+ if (regex.test(name)) {
95
+ return false;
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ // 3. Check schema inclusions (if specified, only entities from these schemas are allowed)
102
+ if (this.options.includeSchemas && this.options.includeSchemas.length > 0) {
103
+ if (!this.options.includeSchemas.some(schema => schema.toLowerCase() === schemaName)) {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ // 4. Check entity inclusions
109
+ if (this.options.includeEntities && this.options.includeEntities.length > 0) {
110
+ // Check for direct match
111
+ if (this.options.includeEntities.includes(name)) {
112
+ return true;
113
+ }
114
+
115
+ // Check for wildcard matches
116
+ for (const pattern of this.options.includeEntities) {
117
+ if (pattern.includes('*')) {
118
+ const regex = new RegExp('^' + pattern.toLowerCase().replace(/\*/g, '.*') + '$');
119
+ if (regex.test(name)) {
120
+ return true;
121
+ }
122
+ }
123
+ }
124
+
125
+ // If include list is specified but no matches found, entity is not allowed
102
126
  return false;
103
- }
104
127
  }
105
- }
128
+
129
+ // By default, allow all entities
130
+ return true;
106
131
  }
107
132
 
108
- // 3. Check schema inclusions (if specified, only entities from these schemas are allowed)
109
- if (this.options.includeSchemas && this.options.includeSchemas.length > 0) {
110
- if (!this.options.includeSchemas.some((schema) => schema.toLowerCase() === schemaName)) {
111
- return false;
112
- }
133
+ /**
134
+ * Set up all the API routes for the REST endpoints
135
+ */
136
+ private setupRoutes() {
137
+ // Middleware to extract MJ user
138
+ this.router.use(this.extractMJUser);
139
+
140
+ // Middleware to check entity allowlist/blocklist
141
+ this.router.use('/entities/:entityName', this.checkEntityAccess.bind(this));
142
+ this.router.use('/views/:entityName', this.checkEntityAccess.bind(this));
143
+ this.router.use('/metadata/entities/:entityName', this.checkEntityAccess.bind(this));
144
+ this.router.use('/users/:userId/favorites/:entityName', this.checkEntityAccess.bind(this));
145
+
146
+ // Entity collection operations
147
+ this.router.get('/entities/:entityName', this.getEntityList.bind(this));
148
+ this.router.post('/entities/:entityName', this.createEntity.bind(this));
149
+
150
+ // Individual entity operations
151
+ this.router.get('/entities/:entityName/:id', this.getEntity.bind(this));
152
+ this.router.put('/entities/:entityName/:id', this.updateEntity.bind(this));
153
+ this.router.delete('/entities/:entityName/:id', this.deleteEntity.bind(this));
154
+
155
+ // Record changes and dependencies
156
+ this.router.get('/entities/:entityName/:id/changes', this.getRecordChanges.bind(this));
157
+ this.router.get('/entities/:entityName/:id/dependencies', this.getRecordDependencies.bind(this));
158
+ this.router.get('/entities/:entityName/:id/name', this.getEntityRecordName.bind(this));
159
+
160
+ // View operations
161
+ this.router.post('/views/:entityName', this.runView.bind(this));
162
+ this.router.post('/views/batch', this.runViews.bind(this));
163
+ this.router.get('/views/entity', this.getViewEntity.bind(this));
164
+
165
+ // Metadata endpoints
166
+ this.router.get('/metadata/entities', this.getEntityMetadata.bind(this));
167
+ this.router.get('/metadata/entities/:entityName', this.getEntityFieldMetadata.bind(this));
168
+
169
+ // Views metadata
170
+ this.router.get('/views/:entityName/metadata', this.getViewsMetadata.bind(this));
171
+
172
+ // User operations
173
+ this.router.get('/users/current', this.getCurrentUser.bind(this));
174
+ this.router.get('/users/:userId/favorites/:entityName/:id', this.getRecordFavoriteStatus.bind(this));
175
+ this.router.put('/users/:userId/favorites/:entityName/:id', this.setRecordFavoriteStatus.bind(this));
176
+ this.router.delete('/users/:userId/favorites/:entityName/:id', this.removeRecordFavoriteStatus.bind(this));
177
+
178
+ // Transaction operations
179
+ this.router.post('/transactions', this.executeTransaction.bind(this));
180
+
181
+ // Reports and queries
182
+ this.router.get('/reports/:reportId', this.runReport.bind(this));
183
+ this.router.post('/queries/run', this.runQuery.bind(this));
184
+
185
+ // Error handling
186
+ this.router.use(this.errorHandler);
113
187
  }
114
188
 
115
- // 4. Check entity inclusions
116
- if (this.options.includeEntities && this.options.includeEntities.length > 0) {
117
- // Check for direct match
118
- if (this.options.includeEntities.includes(name)) {
119
- return true;
120
- }
121
-
122
- // Check for wildcard matches
123
- for (const pattern of this.options.includeEntities) {
124
- if (pattern.includes('*')) {
125
- const regex = new RegExp('^' + pattern.toLowerCase().replace(/\*/g, '.*') + '$');
126
- if (regex.test(name)) {
127
- return true;
128
- }
189
+ /**
190
+ * Middleware to check entity access based on include/exclude lists
191
+ */
192
+ private checkEntityAccess(req: express.Request, res: express.Response, next: express.NextFunction): void {
193
+ const entityName = req.params.entityName;
194
+
195
+ if (!entityName) {
196
+ next();
197
+ return;
129
198
  }
130
- }
131
-
132
- // If include list is specified but no matches found, entity is not allowed
133
- return false;
199
+
200
+ if (!this.isEntityAllowed(entityName)) {
201
+ res.status(403).json({
202
+ error: `Access to entity '${entityName}' is not allowed through the REST API`,
203
+ details: 'This entity is either not included in the allowlist or is explicitly excluded in the REST API configuration'
204
+ });
205
+ return;
206
+ }
207
+
208
+ next();
134
209
  }
135
-
136
- // By default, allow all entities
137
- return true;
138
- }
139
-
140
- /**
141
- * Set up all the API routes for the REST endpoints
142
- */
143
- private setupRoutes() {
144
- // Middleware to extract MJ user
145
- this.router.use(this.extractMJUser);
146
-
147
- // Middleware to check entity allowlist/blocklist
148
- this.router.use('/entities/:entityName', this.checkEntityAccess.bind(this));
149
- this.router.use('/views/:entityName', this.checkEntityAccess.bind(this));
150
- this.router.use('/metadata/entities/:entityName', this.checkEntityAccess.bind(this));
151
- this.router.use('/users/:userId/favorites/:entityName', this.checkEntityAccess.bind(this));
152
-
153
- // Entity collection operations
154
- this.router.get('/entities/:entityName', this.getEntityList.bind(this));
155
- this.router.post('/entities/:entityName', this.createEntity.bind(this));
156
-
157
- // Individual entity operations
158
- this.router.get('/entities/:entityName/:id', this.getEntity.bind(this));
159
- this.router.put('/entities/:entityName/:id', this.updateEntity.bind(this));
160
- this.router.delete('/entities/:entityName/:id', this.deleteEntity.bind(this));
161
-
162
- // Record changes and dependencies
163
- this.router.get('/entities/:entityName/:id/changes', this.getRecordChanges.bind(this));
164
- this.router.get('/entities/:entityName/:id/dependencies', this.getRecordDependencies.bind(this));
165
- this.router.get('/entities/:entityName/:id/name', this.getEntityRecordName.bind(this));
166
-
167
- // View operations
168
- this.router.post('/views/:entityName', this.runView.bind(this));
169
- this.router.post('/views/batch', this.runViews.bind(this));
170
- this.router.get('/views/entity', this.getViewEntity.bind(this));
171
-
172
- // Metadata endpoints
173
- this.router.get('/metadata/entities', this.getEntityMetadata.bind(this));
174
- this.router.get('/metadata/entities/:entityName', this.getEntityFieldMetadata.bind(this));
175
-
176
- // Views metadata
177
- this.router.get('/views/:entityName/metadata', this.getViewsMetadata.bind(this));
178
-
179
- // User operations
180
- this.router.get('/users/current', this.getCurrentUser.bind(this));
181
- this.router.get('/users/:userId/favorites/:entityName/:id', this.getRecordFavoriteStatus.bind(this));
182
- this.router.put('/users/:userId/favorites/:entityName/:id', this.setRecordFavoriteStatus.bind(this));
183
- this.router.delete('/users/:userId/favorites/:entityName/:id', this.removeRecordFavoriteStatus.bind(this));
184
-
185
- // Transaction operations
186
- this.router.post('/transactions', this.executeTransaction.bind(this));
187
-
188
- // Reports and queries
189
- this.router.get('/reports/:reportId', this.runReport.bind(this));
190
- this.router.post('/queries/run', this.runQuery.bind(this));
191
-
192
- // Error handling
193
- this.router.use(this.errorHandler);
194
- }
195
-
196
- /**
197
- * Middleware to check entity access based on include/exclude lists
198
- */
199
- private checkEntityAccess(req: express.Request, res: express.Response, next: express.NextFunction): void {
200
- const entityName = req.params.entityName;
201
-
202
- if (!entityName) {
203
- next();
204
- return;
210
+
211
+ /**
212
+ * Middleware to extract MJ user from request
213
+ */
214
+ private async extractMJUser(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
215
+ try {
216
+ // If authentication middleware has already set req.user with basic info
217
+ if (req['user']) {
218
+ // Get the full MemberJunction user
219
+ const md = new Metadata();
220
+ const userInfo = req['user'];
221
+ // Get user info based on email or ID
222
+ // Note: The actual implementation here would depend on how the MemberJunction core handles user lookup
223
+ // This is a simplification that would need to be implemented properly
224
+ req['mjUser'] = userInfo;
225
+
226
+ if (!req['mjUser']) {
227
+ res.status(401).json({ error: 'User not found in MemberJunction' });
228
+ return;
229
+ }
230
+ } else {
231
+ res.status(401).json({ error: 'Authentication required' });
232
+ return;
233
+ }
234
+
235
+ next();
236
+ } catch (error) {
237
+ next(error);
238
+ }
205
239
  }
206
240
 
207
- if (!this.isEntityAllowed(entityName)) {
208
- res.status(403).json({
209
- error: `Access to entity '${entityName}' is not allowed through the REST API`,
210
- details: 'This entity is either not included in the allowlist or is explicitly excluded in the REST API configuration',
211
- });
212
- return;
241
+ /**
242
+ * Error handling middleware
243
+ */
244
+ private errorHandler(err: any, req: express.Request, res: express.Response, next: express.NextFunction): void {
245
+ LogError(err);
246
+
247
+ if (err.name === 'UnauthorizedError') {
248
+ res.status(401).json({ error: 'Invalid token' });
249
+ return;
250
+ }
251
+
252
+ res.status(500).json({ error: (err as Error)?.message || 'Internal server error' });
213
253
  }
214
254
 
215
- next();
216
- }
217
-
218
- /**
219
- * Middleware to extract MJ user from request
220
- */
221
- private async extractMJUser(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
222
- try {
223
- // If authentication middleware has already set req.user with basic info
224
- if (req['user']) {
225
- // Get the full MemberJunction user
226
- const md = new Metadata();
227
- const userInfo = req['user'];
228
- // Get user info based on email or ID
229
- // Note: The actual implementation here would depend on how the MemberJunction core handles user lookup
230
- // This is a simplification that would need to be implemented properly
231
- req['mjUser'] = userInfo;
232
-
233
- if (!req['mjUser']) {
234
- res.status(401).json({ error: 'User not found in MemberJunction' });
235
- return;
255
+ /**
256
+ * Get the current user
257
+ */
258
+ private async getCurrentUser(req: express.Request, res: express.Response): Promise<void> {
259
+ try {
260
+ const user = req['mjUser'];
261
+
262
+ // Return user info without sensitive data
263
+ res.json({
264
+ ID: user.ID,
265
+ Name: user.Name,
266
+ Email: user.Email,
267
+ FirstName: user.FirstName,
268
+ LastName: user.LastName,
269
+ IsAdmin: user.IsAdmin,
270
+ UserRoles: user.UserRoles.map(role => ({
271
+ ID: role.ID,
272
+ Name: role.Name,
273
+ Description: role.Description
274
+ }))
275
+ });
276
+ } catch (error) {
277
+ LogError(error);
278
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
236
279
  }
237
- } else {
238
- res.status(401).json({ error: 'Authentication required' });
239
- return;
240
- }
241
-
242
- next();
243
- } catch (error) {
244
- next(error);
245
280
  }
246
- }
247
-
248
- /**
249
- * Error handling middleware
250
- */
251
- private errorHandler(err: any, req: express.Request, res: express.Response, next: express.NextFunction): void {
252
- LogError(err);
253
281
 
254
- if (err.name === 'UnauthorizedError') {
255
- res.status(401).json({ error: 'Invalid token' });
256
- return;
282
+ /**
283
+ * Lists entities with optional filtering
284
+ */
285
+ private async getEntityList(req: express.Request, res: express.Response): Promise<void> {
286
+ try {
287
+ const { entityName } = req.params;
288
+ const { filter, orderBy, fields, maxRows, startRow } = req.query;
289
+
290
+ const user = req['mjUser'];
291
+
292
+ // Convert the request to a RunViewParams object
293
+ const params: RunViewParams = {
294
+ EntityName: entityName,
295
+ ExtraFilter: filter as string,
296
+ OrderBy: orderBy as string,
297
+ Fields: fields ? (fields as string).split(',') : undefined,
298
+ MaxRows: maxRows ? parseInt(maxRows as string) : undefined,
299
+ StartRow: startRow ? parseInt(startRow as string) : undefined
300
+ };
301
+
302
+ const result = await ViewOperationsHandler.listEntities(params, user);
303
+ res.json(result);
304
+ } catch (error) {
305
+ LogError(error);
306
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
307
+ }
257
308
  }
258
309
 
259
- res.status(500).json({ error: (err as Error)?.message || 'Internal server error' });
260
- }
261
-
262
- /**
263
- * Get the current user
264
- */
265
- private async getCurrentUser(req: express.Request, res: express.Response): Promise<void> {
266
- try {
267
- const user = req['mjUser'];
268
-
269
- // Return user info without sensitive data
270
- res.json({
271
- ID: user.ID,
272
- Name: user.Name,
273
- Email: user.Email,
274
- FirstName: user.FirstName,
275
- LastName: user.LastName,
276
- IsAdmin: user.IsAdmin,
277
- UserRoles: user.UserRoles.map((role) => ({
278
- ID: role.ID,
279
- Name: role.Name,
280
- Description: role.Description,
281
- })),
282
- });
283
- } catch (error) {
284
- LogError(error);
285
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
286
- }
287
- }
288
-
289
- /**
290
- * Lists entities with optional filtering
291
- */
292
- private async getEntityList(req: express.Request, res: express.Response): Promise<void> {
293
- try {
294
- const { entityName } = req.params;
295
- const { filter, orderBy, fields, maxRows, startRow } = req.query;
296
-
297
- const user = req['mjUser'];
298
-
299
- // Convert the request to a RunViewParams object
300
- const params: RunViewParams = {
301
- EntityName: entityName,
302
- ExtraFilter: filter as string,
303
- OrderBy: orderBy as string,
304
- Fields: fields ? (fields as string).split(',') : undefined,
305
- MaxRows: maxRows ? parseInt(maxRows as string) : undefined,
306
- StartRow: startRow ? parseInt(startRow as string) : undefined,
307
- };
308
-
309
- const result = await ViewOperationsHandler.listEntities(params, user);
310
- res.json(result);
311
- } catch (error) {
312
- LogError(error);
313
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
314
- }
315
- }
316
-
317
- /**
318
- * Get a single entity by ID
319
- */
320
- private async getEntity(req: express.Request, res: express.Response): Promise<void> {
321
- try {
322
- const { entityName, id } = req.params;
323
- const { include } = req.query; // Optional related entities to include
324
-
325
- const user = req['mjUser'];
326
- const relatedEntities = include ? (include as string).split(',') : null;
327
-
328
- const result = await EntityCRUDHandler.getEntity(entityName, id, relatedEntities, user);
329
-
330
- if (result.success) {
331
- res.json(result.entity);
332
- } else {
333
- res.status(result.error.includes('not found') ? 404 : 400).json({ error: result.error });
334
- }
335
- } catch (error) {
336
- LogError(error);
337
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
338
- }
339
- }
340
-
341
- /**
342
- * Create a new entity
343
- */
344
- private async createEntity(req: express.Request, res: express.Response): Promise<void> {
345
- try {
346
- const { entityName } = req.params;
347
- const entityData = req.body;
348
-
349
- const user = req['mjUser'];
350
-
351
- const result = await EntityCRUDHandler.createEntity(entityName, entityData, user);
352
-
353
- if (result.success) {
354
- res.status(201).json(result.entity);
355
- } else {
356
- res.status(400).json({
357
- error: result.error,
358
- details: result.details,
359
- });
360
- }
361
- } catch (error) {
362
- LogError(error);
363
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
364
- }
365
- }
366
-
367
- /**
368
- * Update an existing entity
369
- */
370
- private async updateEntity(req: express.Request, res: express.Response): Promise<void> {
371
- try {
372
- const { entityName, id } = req.params;
373
- const updateData = req.body;
374
-
375
- const user = req['mjUser'];
376
-
377
- const result = await EntityCRUDHandler.updateEntity(entityName, id, updateData, user);
378
-
379
- if (result.success) {
380
- res.json(result.entity);
381
- } else {
382
- res.status(result.error.includes('not found') ? 404 : 400).json({
383
- error: result.error,
384
- details: result.details,
385
- });
386
- }
387
- } catch (error) {
388
- LogError(error);
389
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
390
- }
391
- }
392
-
393
- /**
394
- * Delete an entity
395
- */
396
- private async deleteEntity(req: express.Request, res: express.Response): Promise<void> {
397
- try {
398
- const { entityName, id } = req.params;
399
- const options = req.query.options ? JSON.parse(req.query.options as string) : {};
400
-
401
- const user = req['mjUser'];
402
-
403
- // Convert options to EntityDeleteOptions
404
- const deleteOptions = new EntityDeleteOptions();
405
- if (options.SkipEntityAIActions !== undefined) deleteOptions.SkipEntityAIActions = !!options.SkipEntityAIActions;
406
- if (options.SkipEntityActions !== undefined) deleteOptions.SkipEntityActions = !!options.SkipEntityActions;
407
- if (options.ReplayOnly !== undefined) deleteOptions.ReplayOnly = !!options.ReplayOnly;
408
-
409
- const result = await EntityCRUDHandler.deleteEntity(entityName, id, deleteOptions, user);
410
-
411
- if (result.success) {
412
- res.status(204).send();
413
- } else {
414
- res.status(result.error.includes('not found') ? 404 : 400).json({ error: result.error });
415
- }
416
- } catch (error) {
417
- LogError(error);
418
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
310
+ /**
311
+ * Get a single entity by ID
312
+ */
313
+ private async getEntity(req: express.Request, res: express.Response): Promise<void> {
314
+ try {
315
+ const { entityName, id } = req.params;
316
+ const { include } = req.query; // Optional related entities to include
317
+
318
+ const user = req['mjUser'];
319
+ const relatedEntities = include ? (include as string).split(',') : null;
320
+
321
+ const result = await EntityCRUDHandler.getEntity(entityName, id, relatedEntities, user);
322
+
323
+ if (result.success) {
324
+ res.json(result.entity);
325
+ } else {
326
+ res.status(result.error.includes('not found') ? 404 : 400).json({ error: result.error });
327
+ }
328
+ } catch (error) {
329
+ LogError(error);
330
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
331
+ }
419
332
  }
420
- }
421
-
422
- /**
423
- * Get record changes for an entity
424
- */
425
- private async getRecordChanges(req: express.Request, res: express.Response): Promise<void> {
426
- try {
427
- const { entityName, id } = req.params;
428
- const user = req['mjUser'];
429
-
430
- // Get the entity object
431
- const md = new Metadata();
432
- const entity = await md.GetEntityObject(entityName, user);
433
-
434
- // Create a composite key
435
- const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
436
-
437
- // Use a direct approach for getting record changes
438
- // Note: This is a simplification. The actual implementation may need to be adjusted
439
- // based on how the MemberJunction core handles record changes
440
- const changes = []; // This would be populated with actual record changes
441
-
442
- res.json(changes);
443
- } catch (error) {
444
- LogError(error);
445
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
333
+
334
+ /**
335
+ * Create a new entity
336
+ */
337
+ private async createEntity(req: express.Request, res: express.Response): Promise<void> {
338
+ try {
339
+ const { entityName } = req.params;
340
+ const entityData = req.body;
341
+
342
+ const user = req['mjUser'];
343
+
344
+ const result = await EntityCRUDHandler.createEntity(entityName, entityData, user);
345
+
346
+ if (result.success) {
347
+ res.status(201).json(result.entity);
348
+ } else {
349
+ res.status(400).json({
350
+ error: result.error,
351
+ details: result.details
352
+ });
353
+ }
354
+ } catch (error) {
355
+ LogError(error);
356
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
357
+ }
446
358
  }
447
- }
448
-
449
- /**
450
- * Get record dependencies for an entity
451
- */
452
- private async getRecordDependencies(req: express.Request, res: express.Response): Promise<void> {
453
- try {
454
- const { entityName, id } = req.params;
455
- const user = req['mjUser'];
456
-
457
- // Get the entity object
458
- const md = new Metadata();
459
- const entity = await md.GetEntityObject(entityName, user);
460
-
461
- // Create a composite key
462
- const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
463
-
464
- // Use a direct approach for getting record dependencies
465
- // Note: This is a simplification. The actual implementation may need to be adjusted
466
- // based on how the MemberJunction core handles record dependencies
467
- const dependencies = []; // This would be populated with actual record dependencies
468
-
469
- res.json(dependencies);
470
- } catch (error) {
471
- LogError(error);
472
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
359
+
360
+ /**
361
+ * Update an existing entity
362
+ */
363
+ private async updateEntity(req: express.Request, res: express.Response): Promise<void> {
364
+ try {
365
+ const { entityName, id } = req.params;
366
+ const updateData = req.body;
367
+
368
+ const user = req['mjUser'];
369
+
370
+ const result = await EntityCRUDHandler.updateEntity(entityName, id, updateData, user);
371
+
372
+ if (result.success) {
373
+ res.json(result.entity);
374
+ } else {
375
+ res.status(result.error.includes('not found') ? 404 : 400).json({
376
+ error: result.error,
377
+ details: result.details
378
+ });
379
+ }
380
+ } catch (error) {
381
+ LogError(error);
382
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
383
+ }
473
384
  }
474
- }
475
-
476
- /**
477
- * Get entity record name
478
- */
479
- private async getEntityRecordName(req: express.Request, res: express.Response): Promise<void> {
480
- try {
481
- const { entityName, id } = req.params;
482
- const user = req['mjUser'];
483
-
484
- // Get the entity object
485
- const md = new Metadata();
486
- const entity = await md.GetEntityObject(entityName, user);
487
-
488
- // Create a composite key
489
- const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
490
-
491
- // Use a direct approach for getting entity record name
492
- // Note: This is a simplification. The actual implementation may need to be adjusted
493
- const recordName = 'Record Name'; // This would be populated with the actual record name
494
-
495
- res.json({ recordName });
496
- } catch (error) {
497
- LogError(error);
498
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
385
+
386
+ /**
387
+ * Delete an entity
388
+ */
389
+ private async deleteEntity(req: express.Request, res: express.Response): Promise<void> {
390
+ try {
391
+ const { entityName, id } = req.params;
392
+ const options = req.query.options ? JSON.parse(req.query.options as string) : {};
393
+
394
+ const user = req['mjUser'];
395
+
396
+ // Convert options to EntityDeleteOptions
397
+ const deleteOptions = new EntityDeleteOptions();
398
+ if (options.SkipEntityAIActions !== undefined) deleteOptions.SkipEntityAIActions = !!options.SkipEntityAIActions;
399
+ if (options.SkipEntityActions !== undefined) deleteOptions.SkipEntityActions = !!options.SkipEntityActions;
400
+ if (options.ReplayOnly !== undefined) deleteOptions.ReplayOnly = !!options.ReplayOnly;
401
+
402
+ const result = await EntityCRUDHandler.deleteEntity(entityName, id, deleteOptions, user);
403
+
404
+ if (result.success) {
405
+ res.status(204).send();
406
+ } else {
407
+ res.status(result.error.includes('not found') ? 404 : 400).json({ error: result.error });
408
+ }
409
+ } catch (error) {
410
+ LogError(error);
411
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
412
+ }
499
413
  }
500
- }
501
-
502
- /**
503
- * Run a view
504
- */
505
- private async runView(req: express.Request, res: express.Response): Promise<void> {
506
- try {
507
- const { entityName } = req.params;
508
- const viewParams = req.body;
509
-
510
- const user = req['mjUser'];
511
-
512
- // Create RunViewParams from the request body
513
- const params: RunViewParams = {
514
- EntityName: entityName,
515
- ...viewParams,
516
- };
517
-
518
- const result = await ViewOperationsHandler.runView(params, user);
519
-
520
- if (result.success) {
521
- res.json(result.result);
522
- } else {
523
- res.status(400).json({ error: result.error });
524
- }
525
- } catch (error) {
526
- LogError(error);
527
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
414
+
415
+ /**
416
+ * Get record changes for an entity
417
+ */
418
+ private async getRecordChanges(req: express.Request, res: express.Response): Promise<void> {
419
+ try {
420
+ const { entityName, id } = req.params;
421
+ const user = req['mjUser'];
422
+
423
+ // Get the entity object
424
+ const md = new Metadata();
425
+ const entity = await md.GetEntityObject(entityName, user);
426
+
427
+ // Create a composite key
428
+ const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
429
+
430
+ // Use a direct approach for getting record changes
431
+ // Note: This is a simplification. The actual implementation may need to be adjusted
432
+ // based on how the MemberJunction core handles record changes
433
+ const changes = []; // This would be populated with actual record changes
434
+
435
+ res.json(changes);
436
+ } catch (error) {
437
+ LogError(error);
438
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
439
+ }
528
440
  }
529
- }
530
-
531
- /**
532
- * Run multiple views in batch
533
- */
534
- private async runViews(req: express.Request, res: express.Response): Promise<void> {
535
- try {
536
- const { params } = req.body;
537
- const user = req['mjUser'];
538
-
539
- if (!Array.isArray(params)) {
540
- res.status(400).json({ error: 'params must be an array of RunViewParams' });
541
- return;
542
- }
543
-
544
- // Filter out any views for entities that aren't allowed
545
- // using our enhanced entity filtering with wildcards and schema support
546
- const filteredParams = params.filter((p) => this.isEntityAllowed(p.EntityName));
547
-
548
- // If all requested entities were filtered out, return an error
549
- if (filteredParams.length === 0 && params.length > 0) {
550
- res.status(403).json({
551
- error: 'None of the requested entities are allowed through the REST API',
552
- details:
553
- 'The entities requested are either not included in the allowlist or are explicitly excluded in the REST API configuration',
554
- });
555
- return;
556
- }
557
-
558
- const result = await ViewOperationsHandler.runViews(filteredParams, user);
559
-
560
- if (result.success) {
561
- res.json(result.results);
562
- } else {
563
- res.status(400).json({ error: result.error });
564
- }
565
- } catch (error) {
566
- LogError(error);
567
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
441
+
442
+ /**
443
+ * Get record dependencies for an entity
444
+ */
445
+ private async getRecordDependencies(req: express.Request, res: express.Response): Promise<void> {
446
+ try {
447
+ const { entityName, id } = req.params;
448
+ const user = req['mjUser'];
449
+
450
+ // Get the entity object
451
+ const md = new Metadata();
452
+ const entity = await md.GetEntityObject(entityName, user);
453
+
454
+ // Create a composite key
455
+ const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
456
+
457
+ // Use a direct approach for getting record dependencies
458
+ // Note: This is a simplification. The actual implementation may need to be adjusted
459
+ // based on how the MemberJunction core handles record dependencies
460
+ const dependencies = []; // This would be populated with actual record dependencies
461
+
462
+ res.json(dependencies);
463
+ } catch (error) {
464
+ LogError(error);
465
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
466
+ }
568
467
  }
569
- }
570
-
571
- /**
572
- * Get entity for a view
573
- */
574
- private async getViewEntity(req: express.Request, res: express.Response): Promise<void> {
575
- try {
576
- const { ViewID, ViewName } = req.query;
577
- const user = req['mjUser'];
578
-
579
- if (!ViewID && !ViewName) {
580
- res.status(400).json({ error: 'Either ViewID or ViewName must be provided' });
581
- return;
582
- }
583
-
584
- // Placeholder implementation - this would need to be implemented to lookup view metadata
585
- const entityName = 'SampleEntity'; // This would be determined by looking up the view
586
-
587
- res.json({ entityName });
588
- } catch (error) {
589
- LogError(error);
590
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
468
+
469
+ /**
470
+ * Get entity record name
471
+ */
472
+ private async getEntityRecordName(req: express.Request, res: express.Response): Promise<void> {
473
+ try {
474
+ const { entityName, id } = req.params;
475
+ const user = req['mjUser'];
476
+
477
+ // Get the entity object
478
+ const md = new Metadata();
479
+ const entity = await md.GetEntityObject(entityName, user);
480
+
481
+ // Create a composite key
482
+ const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
483
+
484
+ // Use a direct approach for getting entity record name
485
+ // Note: This is a simplification. The actual implementation may need to be adjusted
486
+ const recordName = "Record Name"; // This would be populated with the actual record name
487
+
488
+ res.json({ recordName });
489
+ } catch (error) {
490
+ LogError(error);
491
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
492
+ }
591
493
  }
592
- }
593
-
594
- /**
595
- * Get metadata for all entities
596
- */
597
- private async getEntityMetadata(req: express.Request, res: express.Response): Promise<void> {
598
- try {
599
- const user = req['mjUser'];
600
-
601
- // Filter entities based on user permissions and REST API configuration
602
- const md = new Metadata();
603
- const entities = md.Entities.filter((e) => {
604
- // First check if entity is allowed based on configuration
605
- if (!this.isEntityAllowed(e.Name)) {
606
- return false;
494
+
495
+ /**
496
+ * Run a view
497
+ */
498
+ private async runView(req: express.Request, res: express.Response): Promise<void> {
499
+ try {
500
+ const { entityName } = req.params;
501
+ const viewParams = req.body;
502
+
503
+ const user = req['mjUser'];
504
+
505
+ // Create RunViewParams from the request body
506
+ const params: RunViewParams = {
507
+ EntityName: entityName,
508
+ ...viewParams
509
+ };
510
+
511
+ const result = await ViewOperationsHandler.runView(params, user);
512
+
513
+ if (result.success) {
514
+ res.json(result.result);
515
+ } else {
516
+ res.status(400).json({ error: result.error });
517
+ }
518
+ } catch (error) {
519
+ LogError(error);
520
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
607
521
  }
522
+ }
608
523
 
609
- // Then check user permissions
610
- const permissions = e.GetUserPermisions(user);
611
- return permissions.CanRead;
612
- });
613
-
614
- const result = entities.map((e) => ({
615
- Name: e.Name,
616
- ClassName: e.ClassName,
617
- SchemaName: e.SchemaName,
618
- DisplayName: e.DisplayName,
619
- Description: e.Description,
620
- IncludeInAPI: e.IncludeInAPI,
621
- AllowCreateAPI: e.AllowCreateAPI,
622
- AllowUpdateAPI: e.AllowUpdateAPI,
623
- AllowDeleteAPI: e.AllowDeleteAPI,
624
- }));
625
-
626
- res.json(result);
627
- } catch (error) {
628
- LogError(error);
629
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
524
+ /**
525
+ * Run multiple views in batch
526
+ */
527
+ private async runViews(req: express.Request, res: express.Response): Promise<void> {
528
+ try {
529
+ const { params } = req.body;
530
+ const user = req['mjUser'];
531
+
532
+ if (!Array.isArray(params)) {
533
+ res.status(400).json({ error: 'params must be an array of RunViewParams' });
534
+ return;
535
+ }
536
+
537
+ // Filter out any views for entities that aren't allowed
538
+ // using our enhanced entity filtering with wildcards and schema support
539
+ const filteredParams = params.filter(p => this.isEntityAllowed(p.EntityName));
540
+
541
+ // If all requested entities were filtered out, return an error
542
+ if (filteredParams.length === 0 && params.length > 0) {
543
+ res.status(403).json({
544
+ error: 'None of the requested entities are allowed through the REST API',
545
+ details: 'The entities requested are either not included in the allowlist or are explicitly excluded in the REST API configuration'
546
+ });
547
+ return;
548
+ }
549
+
550
+ const result = await ViewOperationsHandler.runViews(filteredParams, user);
551
+
552
+ if (result.success) {
553
+ res.json(result.results);
554
+ } else {
555
+ res.status(400).json({ error: result.error });
556
+ }
557
+ } catch (error) {
558
+ LogError(error);
559
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
560
+ }
630
561
  }
631
- }
632
-
633
- /**
634
- * Get field metadata for a specific entity
635
- */
636
- private async getEntityFieldMetadata(req: express.Request, res: express.Response): Promise<void> {
637
- try {
638
- const { entityName } = req.params;
639
-
640
- const user = req['mjUser'];
641
-
642
- const md = new Metadata();
643
- const entity = md.Entities.find((e) => e.Name === entityName);
644
- if (!entity) {
645
- res.status(404).json({ error: `Entity '${entityName}' not found` });
646
- return;
647
- }
648
-
649
- // Check if user can read this entity
650
- const permissions = entity.GetUserPermisions(user);
651
- if (!permissions.CanRead) {
652
- res.status(403).json({ error: 'Permission denied' });
653
- return;
654
- }
655
-
656
- const result = entity.Fields.map((f) => ({
657
- Name: f.Name,
658
- DisplayName: f.DisplayName,
659
- Description: f.Description,
660
- Type: f.Type,
661
- IsRequired: f.AllowsNull === false,
662
- IsPrimaryKey: f.IsPrimaryKey,
663
- IsUnique: f.IsUnique,
664
- MaxLength: f.MaxLength,
665
- DefaultValue: f.DefaultValue,
666
- CodeName: f.CodeName,
667
- TSType: f.TSType,
668
- }));
669
-
670
- res.json(result);
671
- } catch (error) {
672
- LogError(error);
673
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
562
+
563
+ /**
564
+ * Get entity for a view
565
+ */
566
+ private async getViewEntity(req: express.Request, res: express.Response): Promise<void> {
567
+ try {
568
+ const { ViewID, ViewName } = req.query;
569
+ const user = req['mjUser'];
570
+
571
+ if (!ViewID && !ViewName) {
572
+ res.status(400).json({ error: 'Either ViewID or ViewName must be provided' });
573
+ return;
574
+ }
575
+
576
+ // Placeholder implementation - this would need to be implemented to lookup view metadata
577
+ const entityName = "SampleEntity"; // This would be determined by looking up the view
578
+
579
+ res.json({ entityName });
580
+ } catch (error) {
581
+ LogError(error);
582
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
583
+ }
674
584
  }
675
- }
676
585
 
677
- /**
678
- * Get metadata about available views for an entity
679
- */
680
- private async getViewsMetadata(req: express.Request, res: express.Response): Promise<void> {
681
- try {
682
- const { entityName } = req.params;
586
+ /**
587
+ * Get metadata for all entities
588
+ */
589
+ private async getEntityMetadata(req: express.Request, res: express.Response): Promise<void> {
590
+ try {
591
+ const user = req['mjUser'];
592
+
593
+ // Filter entities based on user permissions and REST API configuration
594
+ const md = new Metadata();
595
+ const entities = md.Entities.filter(e => {
596
+ // First check if entity is allowed based on configuration
597
+ if (!this.isEntityAllowed(e.Name)) {
598
+ return false;
599
+ }
600
+
601
+ // Then check user permissions
602
+ const permissions = e.GetUserPermisions(user);
603
+ return permissions.CanRead;
604
+ });
605
+
606
+ const result = entities.map(e => ({
607
+ Name: e.Name,
608
+ ClassName: e.ClassName,
609
+ SchemaName: e.SchemaName,
610
+ DisplayName: e.DisplayName,
611
+ Description: e.Description,
612
+ IncludeInAPI: e.IncludeInAPI,
613
+ AllowCreateAPI: e.AllowCreateAPI,
614
+ AllowUpdateAPI: e.AllowUpdateAPI,
615
+ AllowDeleteAPI: e.AllowDeleteAPI
616
+ }));
617
+
618
+ res.json(result);
619
+ } catch (error) {
620
+ LogError(error);
621
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
622
+ }
623
+ }
683
624
 
684
- const user = req['mjUser'];
625
+ /**
626
+ * Get field metadata for a specific entity
627
+ */
628
+ private async getEntityFieldMetadata(req: express.Request, res: express.Response): Promise<void> {
629
+ try {
630
+ const { entityName } = req.params;
631
+
632
+ const user = req['mjUser'];
633
+
634
+ const md = new Metadata();
635
+ const entity = md.Entities.find(e => e.Name === entityName);
636
+ if (!entity) {
637
+ res.status(404).json({ error: `Entity '${entityName}' not found` });
638
+ return;
639
+ }
640
+
641
+ // Check if user can read this entity
642
+ const permissions = entity.GetUserPermisions(user);
643
+ if (!permissions.CanRead) {
644
+ res.status(403).json({ error: 'Permission denied' });
645
+ return;
646
+ }
647
+
648
+ const result = entity.Fields.map(f => ({
649
+ Name: f.Name,
650
+ DisplayName: f.DisplayName,
651
+ Description: f.Description,
652
+ Type: f.Type,
653
+ IsRequired: f.AllowsNull === false,
654
+ IsPrimaryKey: f.IsPrimaryKey,
655
+ IsUnique: f.IsUnique,
656
+ MaxLength: f.MaxLength,
657
+ DefaultValue: f.DefaultValue,
658
+ CodeName: f.CodeName,
659
+ TSType: f.TSType
660
+ }));
661
+
662
+ res.json(result);
663
+ } catch (error) {
664
+ LogError(error);
665
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
666
+ }
667
+ }
685
668
 
686
- // This would need to be implemented to retrieve available views
687
- // Placeholder implementation
688
- const views = []; // Would need to query available views for this entity
669
+ /**
670
+ * Get metadata about available views for an entity
671
+ */
672
+ private async getViewsMetadata(req: express.Request, res: express.Response): Promise<void> {
673
+ try {
674
+ const { entityName } = req.params;
675
+
676
+ const user = req['mjUser'];
677
+
678
+ // This would need to be implemented to retrieve available views
679
+ // Placeholder implementation
680
+ const views = []; // Would need to query available views for this entity
681
+
682
+ res.json(views);
683
+ } catch (error) {
684
+ LogError(error);
685
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
686
+ }
687
+ }
689
688
 
690
- res.json(views);
691
- } catch (error) {
692
- LogError(error);
693
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
689
+ /**
690
+ * Get favorite status for a record
691
+ */
692
+ private async getRecordFavoriteStatus(req: express.Request, res: express.Response): Promise<void> {
693
+ try {
694
+ const { userId, entityName, id } = req.params;
695
+ const user = req['mjUser'];
696
+
697
+ // Get the entity object
698
+ const md = new Metadata();
699
+ const entity = await md.GetEntityObject(entityName, user);
700
+
701
+ // Create a composite key
702
+ const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
703
+
704
+ // Use a direct approach for getting favorite status
705
+ // Note: This is a simplification. The actual implementation may need to be adjusted
706
+ const isFavorite = false; // This would be populated with the actual favorite status
707
+
708
+ res.json({ isFavorite });
709
+ } catch (error) {
710
+ LogError(error);
711
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
712
+ }
694
713
  }
695
- }
696
-
697
- /**
698
- * Get favorite status for a record
699
- */
700
- private async getRecordFavoriteStatus(req: express.Request, res: express.Response): Promise<void> {
701
- try {
702
- const { userId, entityName, id } = req.params;
703
- const user = req['mjUser'];
704
-
705
- // Get the entity object
706
- const md = new Metadata();
707
- const entity = await md.GetEntityObject(entityName, user);
708
-
709
- // Create a composite key
710
- const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
711
-
712
- // Use a direct approach for getting favorite status
713
- // Note: This is a simplification. The actual implementation may need to be adjusted
714
- const isFavorite = false; // This would be populated with the actual favorite status
715
-
716
- res.json({ isFavorite });
717
- } catch (error) {
718
- LogError(error);
719
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
714
+
715
+ /**
716
+ * Set favorite status for a record
717
+ */
718
+ private async setRecordFavoriteStatus(req: express.Request, res: express.Response): Promise<void> {
719
+ try {
720
+ const { userId, entityName, id } = req.params;
721
+ const user = req['mjUser'];
722
+
723
+ // Get the entity object
724
+ const md = new Metadata();
725
+ const entity = await md.GetEntityObject(entityName, user);
726
+
727
+ // Create a composite key
728
+ const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
729
+
730
+ // Use a direct approach for setting favorite status
731
+ // Note: This is a simplification. The actual implementation may need to be adjusted
732
+ // This would set the favorite status to true
733
+
734
+ res.status(204).send();
735
+ } catch (error) {
736
+ LogError(error);
737
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
738
+ }
720
739
  }
721
- }
722
-
723
- /**
724
- * Set favorite status for a record
725
- */
726
- private async setRecordFavoriteStatus(req: express.Request, res: express.Response): Promise<void> {
727
- try {
728
- const { userId, entityName, id } = req.params;
729
- const user = req['mjUser'];
730
-
731
- // Get the entity object
732
- const md = new Metadata();
733
- const entity = await md.GetEntityObject(entityName, user);
734
-
735
- // Create a composite key
736
- const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
737
-
738
- // Use a direct approach for setting favorite status
739
- // Note: This is a simplification. The actual implementation may need to be adjusted
740
- // This would set the favorite status to true
741
-
742
- res.status(204).send();
743
- } catch (error) {
744
- LogError(error);
745
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
740
+
741
+ /**
742
+ * Remove favorite status for a record
743
+ */
744
+ private async removeRecordFavoriteStatus(req: express.Request, res: express.Response): Promise<void> {
745
+ try {
746
+ const { userId, entityName, id } = req.params;
747
+ const user = req['mjUser'];
748
+
749
+ // Get the entity object
750
+ const md = new Metadata();
751
+ const entity = await md.GetEntityObject(entityName, user);
752
+
753
+ // Create a composite key
754
+ const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
755
+
756
+ // Use a direct approach for setting favorite status
757
+ // Note: This is a simplification. The actual implementation may need to be adjusted
758
+ // This would set the favorite status to false
759
+
760
+ res.status(204).send();
761
+ } catch (error) {
762
+ LogError(error);
763
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
764
+ }
746
765
  }
747
- }
748
-
749
- /**
750
- * Remove favorite status for a record
751
- */
752
- private async removeRecordFavoriteStatus(req: express.Request, res: express.Response): Promise<void> {
753
- try {
754
- const { userId, entityName, id } = req.params;
755
- const user = req['mjUser'];
756
-
757
- // Get the entity object
758
- const md = new Metadata();
759
- const entity = await md.GetEntityObject(entityName, user);
760
-
761
- // Create a composite key
762
- const compositeKey = this.createCompositeKey(entity.EntityInfo, id);
763
-
764
- // Use a direct approach for setting favorite status
765
- // Note: This is a simplification. The actual implementation may need to be adjusted
766
- // This would set the favorite status to false
767
-
768
- res.status(204).send();
769
- } catch (error) {
770
- LogError(error);
771
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
766
+
767
+ /**
768
+ * Execute a transaction
769
+ */
770
+ private async executeTransaction(req: express.Request, res: express.Response): Promise<void> {
771
+ try {
772
+ // Placeholder implementation - this would need to be implemented to handle transactions
773
+ res.status(501).json({ error: 'Not implemented' });
774
+ } catch (error) {
775
+ LogError(error);
776
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
777
+ }
772
778
  }
773
- }
774
-
775
- /**
776
- * Execute a transaction
777
- */
778
- private async executeTransaction(req: express.Request, res: express.Response): Promise<void> {
779
- try {
780
- // Placeholder implementation - this would need to be implemented to handle transactions
781
- res.status(501).json({ error: 'Not implemented' });
782
- } catch (error) {
783
- LogError(error);
784
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
779
+
780
+ /**
781
+ * Run a report
782
+ */
783
+ private async runReport(req: express.Request, res: express.Response): Promise<void> {
784
+ try {
785
+ // Placeholder implementation - this would need to be implemented to run reports
786
+ res.status(501).json({ error: 'Not implemented' });
787
+ } catch (error) {
788
+ LogError(error);
789
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
790
+ }
785
791
  }
786
- }
787
-
788
- /**
789
- * Run a report
790
- */
791
- private async runReport(req: express.Request, res: express.Response): Promise<void> {
792
- try {
793
- // Placeholder implementation - this would need to be implemented to run reports
794
- res.status(501).json({ error: 'Not implemented' });
795
- } catch (error) {
796
- LogError(error);
797
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
792
+
793
+ /**
794
+ * Run a query
795
+ */
796
+ private async runQuery(req: express.Request, res: express.Response): Promise<void> {
797
+ try {
798
+ // Placeholder implementation - this would need to be implemented to run queries
799
+ res.status(501).json({ error: 'Not implemented' });
800
+ } catch (error) {
801
+ LogError(error);
802
+ res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
803
+ }
798
804
  }
799
- }
800
-
801
- /**
802
- * Run a query
803
- */
804
- private async runQuery(req: express.Request, res: express.Response): Promise<void> {
805
- try {
806
- // Placeholder implementation - this would need to be implemented to run queries
807
- res.status(501).json({ error: 'Not implemented' });
808
- } catch (error) {
809
- LogError(error);
810
- res.status(500).json({ error: (error as Error)?.message || 'Unknown error' });
805
+
806
+ /**
807
+ * Helper method to create a composite key from an ID
808
+ */
809
+ private createCompositeKey(entityInfo: EntityInfo, id: string): CompositeKey {
810
+ if (entityInfo.PrimaryKeys.length === 1) {
811
+ // Single primary key
812
+ const primaryKeyField = entityInfo.PrimaryKeys[0].Name;
813
+ const compositeKey = new CompositeKey();
814
+
815
+ // Use key-value pairs instead of SetValue
816
+ compositeKey.KeyValuePairs = [
817
+ { FieldName: primaryKeyField, Value: id }
818
+ ];
819
+
820
+ return compositeKey;
821
+ } else {
822
+ // Composite primary key
823
+ // This is a simplification - in a real implementation, we would need to handle composite keys properly
824
+ throw new Error('Composite primary keys are not supported in this implementation');
825
+ }
811
826
  }
812
- }
813
-
814
- /**
815
- * Helper method to create a composite key from an ID
816
- */
817
- private createCompositeKey(entityInfo: EntityInfo, id: string): CompositeKey {
818
- if (entityInfo.PrimaryKeys.length === 1) {
819
- // Single primary key
820
- const primaryKeyField = entityInfo.PrimaryKeys[0].Name;
821
- const compositeKey = new CompositeKey();
822
-
823
- // Use key-value pairs instead of SetValue
824
- compositeKey.KeyValuePairs = [{ FieldName: primaryKeyField, Value: id }];
825
-
826
- return compositeKey;
827
- } else {
828
- // Composite primary key
829
- // This is a simplification - in a real implementation, we would need to handle composite keys properly
830
- throw new Error('Composite primary keys are not supported in this implementation');
827
+
828
+ /**
829
+ * Get the Express router with all configured routes
830
+ */
831
+ public getRouter(): express.Router {
832
+ return this.router;
831
833
  }
832
- }
833
-
834
- /**
835
- * Get the Express router with all configured routes
836
- */
837
- public getRouter(): express.Router {
838
- return this.router;
839
- }
840
- }
834
+ }