@memberjunction/server 2.112.0 → 2.113.1
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/dist/agents/skip-agent.d.ts +4 -4
- package/dist/agents/skip-agent.d.ts.map +1 -1
- package/dist/agents/skip-agent.js +951 -808
- package/dist/agents/skip-agent.js.map +1 -1
- package/dist/agents/skip-sdk.d.ts +1 -1
- package/dist/agents/skip-sdk.d.ts.map +1 -1
- package/dist/agents/skip-sdk.js +43 -53
- package/dist/agents/skip-sdk.js.map +1 -1
- package/dist/apolloServer/index.js +1 -1
- package/dist/auth/AuthProviderFactory.d.ts +1 -1
- package/dist/auth/AuthProviderFactory.d.ts.map +1 -1
- package/dist/auth/AuthProviderFactory.js +3 -1
- package/dist/auth/AuthProviderFactory.js.map +1 -1
- package/dist/auth/BaseAuthProvider.d.ts +1 -1
- package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
- package/dist/auth/BaseAuthProvider.js +2 -3
- package/dist/auth/BaseAuthProvider.js.map +1 -1
- package/dist/auth/IAuthProvider.d.ts +1 -1
- package/dist/auth/IAuthProvider.d.ts.map +1 -1
- package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
- package/dist/auth/exampleNewUserSubClass.js +1 -1
- package/dist/auth/exampleNewUserSubClass.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +6 -6
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.js +1 -1
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/newUsers.d.ts +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +7 -7
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/auth/providers/Auth0Provider.d.ts +1 -1
- package/dist/auth/providers/Auth0Provider.d.ts.map +1 -1
- package/dist/auth/providers/Auth0Provider.js +1 -1
- package/dist/auth/providers/Auth0Provider.js.map +1 -1
- package/dist/auth/providers/CognitoProvider.d.ts +1 -1
- package/dist/auth/providers/CognitoProvider.d.ts.map +1 -1
- package/dist/auth/providers/CognitoProvider.js +6 -3
- package/dist/auth/providers/CognitoProvider.js.map +1 -1
- package/dist/auth/providers/GoogleProvider.d.ts +1 -1
- package/dist/auth/providers/GoogleProvider.d.ts.map +1 -1
- package/dist/auth/providers/GoogleProvider.js +1 -1
- package/dist/auth/providers/GoogleProvider.js.map +1 -1
- package/dist/auth/providers/MSALProvider.d.ts +1 -1
- package/dist/auth/providers/MSALProvider.d.ts.map +1 -1
- package/dist/auth/providers/MSALProvider.js +1 -1
- package/dist/auth/providers/MSALProvider.js.map +1 -1
- package/dist/auth/providers/OktaProvider.d.ts +1 -1
- package/dist/auth/providers/OktaProvider.d.ts.map +1 -1
- package/dist/auth/providers/OktaProvider.js +1 -1
- package/dist/auth/providers/OktaProvider.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -22
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +7 -9
- package/dist/context.js.map +1 -1
- package/dist/entitySubclasses/entityPermissions.server.d.ts +1 -1
- package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
- package/dist/entitySubclasses/entityPermissions.server.js +1 -1
- package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
- package/dist/generated/generated.d.ts +788 -658
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +2050 -3054
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/KeyInputOutputTypes.d.ts +1 -1
- package/dist/generic/KeyInputOutputTypes.d.ts.map +1 -1
- package/dist/generic/KeyInputOutputTypes.js +1 -1
- package/dist/generic/KeyInputOutputTypes.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +1 -1
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +10 -15
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +15 -15
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -18
- package/dist/index.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +2 -2
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +30 -28
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +2 -2
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +50 -60
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +38 -36
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/CreateQueryResolver.js +40 -43
- package/dist/resolvers/CreateQueryResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +1 -1
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/EntityResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityResolver.js +1 -1
- package/dist/resolvers/EntityResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +1 -1
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.js +1 -1
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +5 -5
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +6 -8
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +3 -3
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +11 -11
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/ReportResolver.js +1 -1
- package/dist/resolvers/ReportResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +28 -27
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +31 -31
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +9 -9
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +10 -10
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +14 -15
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +44 -48
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TaskResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskResolver.js +7 -7
- package/dist/resolvers/TaskResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +12 -12
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.d.ts +1 -1
- package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.js +1 -1
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +16 -14
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +25 -23
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +21 -17
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
- package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
- package/dist/services/ScheduledJobsService.d.ts.map +1 -1
- package/dist/services/ScheduledJobsService.js +6 -4
- package/dist/services/ScheduledJobsService.js.map +1 -1
- package/dist/services/TaskOrchestrator.d.ts +1 -1
- package/dist/services/TaskOrchestrator.d.ts.map +1 -1
- package/dist/services/TaskOrchestrator.js +30 -30
- package/dist/services/TaskOrchestrator.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +2 -2
- package/dist/util.js.map +1 -1
- package/package.json +39 -36
- package/src/agents/skip-agent.ts +1200 -1067
- package/src/agents/skip-sdk.ts +851 -877
- package/src/apolloServer/index.ts +2 -2
- package/src/auth/AuthProviderFactory.ts +14 -8
- package/src/auth/BaseAuthProvider.ts +4 -5
- package/src/auth/IAuthProvider.ts +2 -2
- package/src/auth/exampleNewUserSubClass.ts +2 -9
- package/src/auth/index.ts +26 -31
- package/src/auth/initializeProviders.ts +3 -3
- package/src/auth/newUsers.ts +134 -166
- package/src/auth/providers/Auth0Provider.ts +5 -5
- package/src/auth/providers/CognitoProvider.ts +10 -7
- package/src/auth/providers/GoogleProvider.ts +5 -4
- package/src/auth/providers/MSALProvider.ts +5 -5
- package/src/auth/providers/OktaProvider.ts +7 -6
- package/src/config.ts +54 -63
- package/src/context.ts +30 -42
- package/src/entitySubclasses/entityPermissions.server.ts +3 -3
- package/src/generated/generated.ts +40442 -48106
- package/src/generic/KeyInputOutputTypes.ts +6 -3
- package/src/generic/ResolverBase.ts +78 -119
- package/src/generic/RunViewResolver.ts +23 -27
- package/src/index.ts +48 -66
- package/src/resolvers/ActionResolver.ts +57 -46
- package/src/resolvers/AskSkipResolver.ts +533 -607
- package/src/resolvers/ComponentRegistryResolver.ts +562 -547
- package/src/resolvers/CreateQueryResolver.ts +655 -683
- package/src/resolvers/DatasetResolver.ts +6 -5
- package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
- package/src/resolvers/EntityRecordNameResolver.ts +5 -9
- package/src/resolvers/EntityResolver.ts +7 -9
- package/src/resolvers/FileCategoryResolver.ts +2 -2
- package/src/resolvers/FileResolver.ts +4 -4
- package/src/resolvers/GetDataContextDataResolver.ts +118 -106
- package/src/resolvers/GetDataResolver.ts +205 -194
- package/src/resolvers/MergeRecordsResolver.ts +5 -5
- package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
- package/src/resolvers/QueryResolver.ts +78 -95
- package/src/resolvers/ReportResolver.ts +2 -2
- package/src/resolvers/RunAIAgentResolver.ts +828 -818
- package/src/resolvers/RunAIPromptResolver.ts +709 -693
- package/src/resolvers/RunTemplateResolver.ts +103 -105
- package/src/resolvers/SqlLoggingConfigResolver.ts +72 -69
- package/src/resolvers/SyncDataResolver.ts +352 -386
- package/src/resolvers/SyncRolesUsersResolver.ts +350 -387
- package/src/resolvers/TaskResolver.ts +115 -110
- package/src/resolvers/TransactionGroupResolver.ts +138 -143
- package/src/resolvers/UserFavoriteResolver.ts +8 -17
- package/src/resolvers/UserViewResolver.ts +12 -17
- package/src/rest/EntityCRUDHandler.ts +268 -291
- package/src/rest/RESTEndpointHandler.ts +776 -782
- package/src/rest/ViewOperationsHandler.ts +195 -191
- package/src/scheduler/LearningCycleScheduler.ts +52 -8
- package/src/services/ScheduledJobsService.ts +132 -129
- package/src/services/TaskOrchestrator.ts +776 -792
- package/src/types.ts +9 -15
- package/src/util.ts +109 -112
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
52
|
+
constructor(options: RESTEndpointHandlerOptions = {}) {
|
|
53
|
+
this.router = express.Router();
|
|
54
|
+
this.options = options;
|
|
55
|
+
this.setupRoutes();
|
|
88
56
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
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
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
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
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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
|
+
}
|