@memberjunction/server 4.1.0 → 4.3.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.
- package/dist/generated/generated.d.ts +6 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +26 -0
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/DeleteOptionsInput.d.ts +14 -1
- package/dist/generic/DeleteOptionsInput.d.ts.map +1 -1
- package/dist/generic/DeleteOptionsInput.js +10 -1
- package/dist/generic/DeleteOptionsInput.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/CreateQueryResolver.js +3 -1
- package/dist/resolvers/CreateQueryResolver.js.map +1 -1
- package/dist/resolvers/ISAEntityResolver.d.ts +32 -0
- package/dist/resolvers/ISAEntityResolver.d.ts.map +1 -0
- package/dist/resolvers/ISAEntityResolver.js +109 -0
- package/dist/resolvers/ISAEntityResolver.js.map +1 -0
- package/package.json +56 -59
- package/src/__tests__/setup.ts +8 -10
- package/src/auth/__tests__/backward-compatibility.test.ts +74 -85
- package/src/generated/generated.ts +20 -0
- package/src/generic/DeleteOptionsInput.ts +20 -3
- package/src/index.ts +1 -0
- package/src/resolvers/CreateQueryResolver.ts +3 -1
- package/src/resolvers/ISAEntityResolver.ts +96 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "MemberJunction: This project provides API access via GraphQL to the common data store.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -18,65 +18,65 @@
|
|
|
18
18
|
"build": "tsc && tsc-alias -f",
|
|
19
19
|
"bundle": "tsc -noEmit && pkgroll --sourcemap --minify",
|
|
20
20
|
"watch": "tsc -w",
|
|
21
|
-
"test": "
|
|
22
|
-
"test:watch": "
|
|
23
|
-
"test:coverage": "
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"test:coverage": "vitest run --coverage"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@apollo/server": "^4.9.1",
|
|
27
27
|
"@graphql-tools/schema": "latest",
|
|
28
28
|
"@graphql-tools/utils": "^11.0.0",
|
|
29
|
-
"@memberjunction/actions": "4.
|
|
30
|
-
"@memberjunction/actions-base": "4.
|
|
31
|
-
"@memberjunction/actions-apollo": "4.
|
|
32
|
-
"@memberjunction/actions-bizapps-accounting": "4.
|
|
33
|
-
"@memberjunction/actions-bizapps-crm": "4.
|
|
34
|
-
"@memberjunction/actions-bizapps-formbuilders": "4.
|
|
35
|
-
"@memberjunction/actions-bizapps-lms": "4.
|
|
36
|
-
"@memberjunction/actions-bizapps-social": "4.
|
|
37
|
-
"@memberjunction/ai": "4.
|
|
38
|
-
"@memberjunction/ai-mcp-client": "4.
|
|
39
|
-
"@memberjunction/ai-agent-manager": "4.
|
|
40
|
-
"@memberjunction/ai-agent-manager-actions": "4.
|
|
41
|
-
"@memberjunction/ai-agents": "4.
|
|
42
|
-
"@memberjunction/ai-core-plus": "4.
|
|
43
|
-
"@memberjunction/ai-prompts": "4.
|
|
44
|
-
"@memberjunction/ai-provider-bundle": "4.
|
|
45
|
-
"@memberjunction/ai-vectors-pinecone": "4.
|
|
46
|
-
"@memberjunction/aiengine": "4.
|
|
47
|
-
"@memberjunction/communication-ms-graph": "4.
|
|
48
|
-
"@memberjunction/communication-sendgrid": "4.
|
|
49
|
-
"@memberjunction/communication-types": "4.
|
|
50
|
-
"@memberjunction/component-registry-client-sdk": "4.
|
|
51
|
-
"@memberjunction/config": "4.
|
|
52
|
-
"@memberjunction/core": "4.
|
|
53
|
-
"@memberjunction/core-actions": "4.
|
|
54
|
-
"@memberjunction/core-entities": "4.
|
|
55
|
-
"@memberjunction/core-entities-server": "4.
|
|
56
|
-
"@memberjunction/data-context": "4.
|
|
57
|
-
"@memberjunction/data-context-server": "4.
|
|
58
|
-
"@memberjunction/doc-utils": "4.
|
|
59
|
-
"@memberjunction/api-keys": "4.
|
|
60
|
-
"@memberjunction/encryption": "4.
|
|
61
|
-
"@memberjunction/entity-communications-base": "4.
|
|
62
|
-
"@memberjunction/entity-communications-server": "4.
|
|
63
|
-
"@memberjunction/external-change-detection": "4.
|
|
64
|
-
"@memberjunction/global": "4.
|
|
65
|
-
"@memberjunction/graphql-dataprovider": "4.
|
|
66
|
-
"@memberjunction/interactive-component-types": "4.
|
|
67
|
-
"@memberjunction/notifications": "4.
|
|
68
|
-
"@memberjunction/queue": "4.
|
|
69
|
-
"@memberjunction/scheduling-actions": "4.
|
|
70
|
-
"@memberjunction/scheduling-base-types": "4.
|
|
71
|
-
"@memberjunction/scheduling-engine": "4.
|
|
72
|
-
"@memberjunction/scheduling-engine-base": "4.
|
|
73
|
-
"@memberjunction/skip-types": "4.
|
|
74
|
-
"@memberjunction/sqlserver-dataprovider": "4.
|
|
75
|
-
"@memberjunction/storage": "4.
|
|
76
|
-
"@memberjunction/templates": "4.
|
|
77
|
-
"@memberjunction/testing-engine": "4.
|
|
78
|
-
"@memberjunction/testing-engine-base": "4.
|
|
79
|
-
"@memberjunction/version-history": "4.
|
|
29
|
+
"@memberjunction/actions": "4.3.0",
|
|
30
|
+
"@memberjunction/actions-base": "4.3.0",
|
|
31
|
+
"@memberjunction/actions-apollo": "4.3.0",
|
|
32
|
+
"@memberjunction/actions-bizapps-accounting": "4.3.0",
|
|
33
|
+
"@memberjunction/actions-bizapps-crm": "4.3.0",
|
|
34
|
+
"@memberjunction/actions-bizapps-formbuilders": "4.3.0",
|
|
35
|
+
"@memberjunction/actions-bizapps-lms": "4.3.0",
|
|
36
|
+
"@memberjunction/actions-bizapps-social": "4.3.0",
|
|
37
|
+
"@memberjunction/ai": "4.3.0",
|
|
38
|
+
"@memberjunction/ai-mcp-client": "4.3.0",
|
|
39
|
+
"@memberjunction/ai-agent-manager": "4.3.0",
|
|
40
|
+
"@memberjunction/ai-agent-manager-actions": "4.3.0",
|
|
41
|
+
"@memberjunction/ai-agents": "4.3.0",
|
|
42
|
+
"@memberjunction/ai-core-plus": "4.3.0",
|
|
43
|
+
"@memberjunction/ai-prompts": "4.3.0",
|
|
44
|
+
"@memberjunction/ai-provider-bundle": "4.3.0",
|
|
45
|
+
"@memberjunction/ai-vectors-pinecone": "4.3.0",
|
|
46
|
+
"@memberjunction/aiengine": "4.3.0",
|
|
47
|
+
"@memberjunction/communication-ms-graph": "4.3.0",
|
|
48
|
+
"@memberjunction/communication-sendgrid": "4.3.0",
|
|
49
|
+
"@memberjunction/communication-types": "4.3.0",
|
|
50
|
+
"@memberjunction/component-registry-client-sdk": "4.3.0",
|
|
51
|
+
"@memberjunction/config": "4.3.0",
|
|
52
|
+
"@memberjunction/core": "4.3.0",
|
|
53
|
+
"@memberjunction/core-actions": "4.3.0",
|
|
54
|
+
"@memberjunction/core-entities": "4.3.0",
|
|
55
|
+
"@memberjunction/core-entities-server": "4.3.0",
|
|
56
|
+
"@memberjunction/data-context": "4.3.0",
|
|
57
|
+
"@memberjunction/data-context-server": "4.3.0",
|
|
58
|
+
"@memberjunction/doc-utils": "4.3.0",
|
|
59
|
+
"@memberjunction/api-keys": "4.3.0",
|
|
60
|
+
"@memberjunction/encryption": "4.3.0",
|
|
61
|
+
"@memberjunction/entity-communications-base": "4.3.0",
|
|
62
|
+
"@memberjunction/entity-communications-server": "4.3.0",
|
|
63
|
+
"@memberjunction/external-change-detection": "4.3.0",
|
|
64
|
+
"@memberjunction/global": "4.3.0",
|
|
65
|
+
"@memberjunction/graphql-dataprovider": "4.3.0",
|
|
66
|
+
"@memberjunction/interactive-component-types": "4.3.0",
|
|
67
|
+
"@memberjunction/notifications": "4.3.0",
|
|
68
|
+
"@memberjunction/queue": "4.3.0",
|
|
69
|
+
"@memberjunction/scheduling-actions": "4.3.0",
|
|
70
|
+
"@memberjunction/scheduling-base-types": "4.3.0",
|
|
71
|
+
"@memberjunction/scheduling-engine": "4.3.0",
|
|
72
|
+
"@memberjunction/scheduling-engine-base": "4.3.0",
|
|
73
|
+
"@memberjunction/skip-types": "4.3.0",
|
|
74
|
+
"@memberjunction/sqlserver-dataprovider": "4.3.0",
|
|
75
|
+
"@memberjunction/storage": "4.3.0",
|
|
76
|
+
"@memberjunction/templates": "4.3.0",
|
|
77
|
+
"@memberjunction/testing-engine": "4.3.0",
|
|
78
|
+
"@memberjunction/testing-engine-base": "4.3.0",
|
|
79
|
+
"@memberjunction/version-history": "4.3.0",
|
|
80
80
|
"@types/compression": "^1.8.1",
|
|
81
81
|
"@types/cors": "^2.8.19",
|
|
82
82
|
"@types/jsonwebtoken": "9.0.10",
|
|
@@ -110,11 +110,8 @@
|
|
|
110
110
|
"zod": "~3.24.4"
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
|
-
"@jest/globals": "^30.2.0",
|
|
114
|
-
"@types/jest": "^30.0.0",
|
|
115
|
-
"jest": "^30.2.0",
|
|
116
113
|
"pkgroll": "2.23.0",
|
|
117
|
-
"
|
|
114
|
+
"vitest": "^3.1.1",
|
|
118
115
|
"typescript": "^5.9.3"
|
|
119
116
|
},
|
|
120
117
|
"repository": {
|
package/src/__tests__/setup.ts
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Vitest setup file for MJServer tests
|
|
3
4
|
* Runs before each test file
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
// Increase timeout for async operations
|
|
7
|
-
|
|
8
|
-
if (typeof jest !== 'undefined') {
|
|
9
|
-
jest.setTimeout(30000);
|
|
10
|
-
}
|
|
8
|
+
vi.setConfig({ testTimeout: 30000 });
|
|
11
9
|
|
|
12
10
|
// Mock console methods to reduce noise (can be enabled per test)
|
|
13
11
|
// Uncomment to silence logs during tests
|
|
14
12
|
// global.console = {
|
|
15
13
|
// ...console,
|
|
16
|
-
// log:
|
|
17
|
-
// debug:
|
|
18
|
-
// info:
|
|
19
|
-
// warn:
|
|
20
|
-
// error:
|
|
14
|
+
// log: vi.fn(),
|
|
15
|
+
// debug: vi.fn(),
|
|
16
|
+
// info: vi.fn(),
|
|
17
|
+
// warn: vi.fn(),
|
|
18
|
+
// error: vi.fn(),
|
|
21
19
|
// };
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach,
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock the config module before any imports that depend on it
|
|
4
|
+
vi.mock('../../config', () => ({
|
|
5
|
+
configInfo: {
|
|
6
|
+
authProviders: [],
|
|
7
|
+
databaseSettings: {},
|
|
8
|
+
graphqlPort: 4000,
|
|
9
|
+
baseUrl: 'http://localhost',
|
|
10
|
+
graphqlRootPath: '/',
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
2
14
|
import { AuthProviderFactory } from '../AuthProviderFactory';
|
|
3
15
|
import { IAuthProvider } from '../IAuthProvider';
|
|
4
16
|
import { initializeAuthProviders } from '../initializeProviders';
|
|
@@ -18,36 +30,32 @@ describe('Authentication Provider Backward Compatibility', () => {
|
|
|
18
30
|
factory.clear();
|
|
19
31
|
});
|
|
20
32
|
|
|
21
|
-
describe('
|
|
22
|
-
it('should
|
|
23
|
-
// Simulate legacy environment variables
|
|
24
|
-
process.env.TENANT_ID = 'test-tenant-id';
|
|
25
|
-
process.env.WEB_CLIENT_ID = 'test-client-id';
|
|
26
|
-
|
|
27
|
-
// Initialize with legacy config
|
|
33
|
+
describe('Configuration-Based Provider Initialization', () => {
|
|
34
|
+
it('should initialize with no providers when config is empty', () => {
|
|
28
35
|
initializeAuthProviders();
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
expect(msalProvider).toBeDefined();
|
|
33
|
-
expect(msalProvider?.issuer).toContain('test-tenant-id');
|
|
34
|
-
expect(msalProvider?.audience).toBe('test-client-id');
|
|
36
|
+
|
|
37
|
+
// With empty authProviders array, no providers should be registered
|
|
38
|
+
expect(factory.hasProviders()).toBe(false);
|
|
35
39
|
});
|
|
36
|
-
|
|
37
|
-
it('should
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
|
|
41
|
+
it('should clear existing providers before re-initializing', () => {
|
|
42
|
+
// Register a manual provider first
|
|
43
|
+
const testProvider = {
|
|
44
|
+
name: 'test',
|
|
45
|
+
issuer: 'https://test.com',
|
|
46
|
+
audience: 'test',
|
|
47
|
+
jwksUri: 'https://test.com/jwks',
|
|
48
|
+
validateConfig: () => true,
|
|
49
|
+
getSigningKey: vi.fn(),
|
|
50
|
+
extractUserInfo: vi.fn(),
|
|
51
|
+
matchesIssuer: vi.fn(),
|
|
52
|
+
} as IAuthProvider;
|
|
53
|
+
factory.register(testProvider);
|
|
54
|
+
expect(factory.hasProviders()).toBe(true);
|
|
55
|
+
|
|
56
|
+
// Re-initialize clears everything
|
|
44
57
|
initializeAuthProviders();
|
|
45
|
-
|
|
46
|
-
// Check that Auth0 provider was created
|
|
47
|
-
const auth0Provider = factory.getByName('auth0');
|
|
48
|
-
expect(auth0Provider).toBeDefined();
|
|
49
|
-
expect(auth0Provider?.issuer).toBe('https://test.auth0.com/');
|
|
50
|
-
expect(auth0Provider?.audience).toBe('auth0-client-id');
|
|
58
|
+
expect(factory.hasProviders()).toBe(false);
|
|
51
59
|
});
|
|
52
60
|
});
|
|
53
61
|
|
|
@@ -61,8 +69,8 @@ describe('Authentication Provider Backward Compatibility', () => {
|
|
|
61
69
|
audience: 'test-audience',
|
|
62
70
|
jwksUri: 'https://test.provider.com/.well-known/jwks.json',
|
|
63
71
|
validateConfig: () => true,
|
|
64
|
-
getSigningKey:
|
|
65
|
-
extractUserInfo:
|
|
72
|
+
getSigningKey: vi.fn(),
|
|
73
|
+
extractUserInfo: vi.fn(),
|
|
66
74
|
matchesIssuer: (issuer: string) => {
|
|
67
75
|
const normalized = issuer.toLowerCase().replace(/\/$/, '');
|
|
68
76
|
return normalized === 'https://test.provider.com/oauth2';
|
|
@@ -88,9 +96,9 @@ describe('Authentication Provider Backward Compatibility', () => {
|
|
|
88
96
|
audience: 'test',
|
|
89
97
|
jwksUri: 'https://test.provider.com/jwks',
|
|
90
98
|
validateConfig: () => true,
|
|
91
|
-
getSigningKey:
|
|
92
|
-
extractUserInfo:
|
|
93
|
-
matchesIssuer:
|
|
99
|
+
getSigningKey: vi.fn(),
|
|
100
|
+
extractUserInfo: vi.fn(),
|
|
101
|
+
matchesIssuer: vi.fn((issuer: string): boolean => issuer === 'https://test.provider.com')
|
|
94
102
|
} as IAuthProvider;
|
|
95
103
|
|
|
96
104
|
factory.register(testProvider);
|
|
@@ -106,55 +114,36 @@ describe('Authentication Provider Backward Compatibility', () => {
|
|
|
106
114
|
});
|
|
107
115
|
|
|
108
116
|
describe('User Info Extraction', () => {
|
|
109
|
-
it('should
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Initialize providers
|
|
140
|
-
initializeAuthProviders();
|
|
141
|
-
|
|
142
|
-
// Test extraction for each provider type
|
|
143
|
-
const msalProvider = factory.getByIssuer(msalPayload.iss);
|
|
144
|
-
if (msalProvider) {
|
|
145
|
-
const msalUserInfo = msalProvider.extractUserInfo(msalPayload);
|
|
146
|
-
expect(msalUserInfo.email).toBe('user@example.com');
|
|
147
|
-
expect(msalUserInfo.firstName).toBe('John');
|
|
148
|
-
expect(msalUserInfo.lastName).toBe('Doe');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const auth0Provider = factory.getByIssuer(auth0Payload.iss);
|
|
152
|
-
if (auth0Provider) {
|
|
153
|
-
const auth0UserInfo = auth0Provider.extractUserInfo(auth0Payload);
|
|
154
|
-
expect(auth0UserInfo.email).toBe('user@example.com');
|
|
155
|
-
expect(auth0UserInfo.firstName).toBe('Jane');
|
|
156
|
-
expect(auth0UserInfo.lastName).toBe('Smith');
|
|
157
|
-
}
|
|
117
|
+
it('should return undefined for unregistered issuer', () => {
|
|
118
|
+
const provider = factory.getByIssuer('https://unknown.issuer.com');
|
|
119
|
+
expect(provider).toBeUndefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should support extractUserInfo on manually registered providers', () => {
|
|
123
|
+
const mockExtract = vi.fn().mockReturnValue({
|
|
124
|
+
email: 'user@test.com',
|
|
125
|
+
firstName: 'Test',
|
|
126
|
+
lastName: 'User',
|
|
127
|
+
});
|
|
128
|
+
const testProvider = {
|
|
129
|
+
name: 'test-extract',
|
|
130
|
+
issuer: 'https://test-extract.com',
|
|
131
|
+
audience: 'test',
|
|
132
|
+
jwksUri: 'https://test-extract.com/jwks',
|
|
133
|
+
validateConfig: () => true,
|
|
134
|
+
getSigningKey: vi.fn(),
|
|
135
|
+
extractUserInfo: mockExtract,
|
|
136
|
+
matchesIssuer: (iss: string) => iss === 'https://test-extract.com',
|
|
137
|
+
} as IAuthProvider;
|
|
138
|
+
|
|
139
|
+
factory.register(testProvider);
|
|
140
|
+
|
|
141
|
+
const found = factory.getByIssuer('https://test-extract.com');
|
|
142
|
+
expect(found).toBeDefined();
|
|
143
|
+
|
|
144
|
+
const userInfo = found!.extractUserInfo({ email: 'user@test.com' });
|
|
145
|
+
expect(userInfo.email).toBe('user@test.com');
|
|
146
|
+
expect(mockExtract).toHaveBeenCalledTimes(1);
|
|
158
147
|
});
|
|
159
148
|
});
|
|
160
149
|
|
|
@@ -172,9 +161,9 @@ describe('Authentication Provider Backward Compatibility', () => {
|
|
|
172
161
|
audience: 'test',
|
|
173
162
|
jwksUri: 'https://test.com/jwks',
|
|
174
163
|
validateConfig: () => false,
|
|
175
|
-
getSigningKey:
|
|
176
|
-
extractUserInfo:
|
|
177
|
-
matchesIssuer:
|
|
164
|
+
getSigningKey: vi.fn(),
|
|
165
|
+
extractUserInfo: vi.fn(),
|
|
166
|
+
matchesIssuer: vi.fn()
|
|
178
167
|
} as IAuthProvider;
|
|
179
168
|
|
|
180
169
|
expect(() => factory.register(invalidProvider)).toThrow();
|
|
@@ -59681,6 +59681,14 @@ export class MJSchemaInfo_ {
|
|
|
59681
59681
|
@Field({nullable: true})
|
|
59682
59682
|
Description?: string;
|
|
59683
59683
|
|
|
59684
|
+
@Field({nullable: true, description: `Optional prefix to prepend to entity names generated for this schema. For example, setting this to "Committees: " would result in entity names like "Committees: Individuals". Can be overridden by mj.config.cjs NameRulesBySchema settings.`})
|
|
59685
|
+
@MaxLength(50)
|
|
59686
|
+
EntityNamePrefix?: string;
|
|
59687
|
+
|
|
59688
|
+
@Field({nullable: true, description: `Optional suffix to append to entity names generated for this schema. Can be overridden by mj.config.cjs NameRulesBySchema settings.`})
|
|
59689
|
+
@MaxLength(50)
|
|
59690
|
+
EntityNameSuffix?: string;
|
|
59691
|
+
|
|
59684
59692
|
}
|
|
59685
59693
|
|
|
59686
59694
|
//****************************************************************************
|
|
@@ -59705,6 +59713,12 @@ export class CreateMJSchemaInfoInput {
|
|
|
59705
59713
|
|
|
59706
59714
|
@Field({ nullable: true })
|
|
59707
59715
|
Description: string | null;
|
|
59716
|
+
|
|
59717
|
+
@Field({ nullable: true })
|
|
59718
|
+
EntityNamePrefix: string | null;
|
|
59719
|
+
|
|
59720
|
+
@Field({ nullable: true })
|
|
59721
|
+
EntityNameSuffix: string | null;
|
|
59708
59722
|
}
|
|
59709
59723
|
|
|
59710
59724
|
|
|
@@ -59731,6 +59745,12 @@ export class UpdateMJSchemaInfoInput {
|
|
|
59731
59745
|
@Field({ nullable: true })
|
|
59732
59746
|
Description?: string | null;
|
|
59733
59747
|
|
|
59748
|
+
@Field({ nullable: true })
|
|
59749
|
+
EntityNamePrefix?: string | null;
|
|
59750
|
+
|
|
59751
|
+
@Field({ nullable: true })
|
|
59752
|
+
EntityNameSuffix?: string | null;
|
|
59753
|
+
|
|
59734
59754
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
59735
59755
|
OldValues___?: KeyValuePairInput[];
|
|
59736
59756
|
}
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { Field, InputType } from "type-graphql";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* GraphQL InputType for the DeleteOptions
|
|
4
|
+
* GraphQL InputType for the DeleteOptions.
|
|
5
|
+
* Must be kept in sync with EntityDeleteOptions in @memberjunction/core.
|
|
5
6
|
*/
|
|
6
7
|
@InputType()
|
|
7
8
|
export class DeleteOptionsInput {
|
|
8
9
|
@Field(() => Boolean)
|
|
9
10
|
SkipEntityAIActions: boolean;
|
|
10
|
-
|
|
11
|
-
@Field(() => Boolean)
|
|
11
|
+
|
|
12
|
+
@Field(() => Boolean)
|
|
12
13
|
SkipEntityActions: boolean;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* When set to true, the delete operation will BYPASS Validate() and the actual
|
|
17
|
+
* process of deleting the record from the database but WILL invoke any associated
|
|
18
|
+
* actions (AI Actions, Entity Actions, etc...).
|
|
19
|
+
*/
|
|
20
|
+
@Field(() => Boolean)
|
|
21
|
+
ReplayOnly: boolean;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* When true, this entity is being deleted as part of an IS-A parent chain
|
|
25
|
+
* initiated by a child entity. The child deletes itself first (FK constraint),
|
|
26
|
+
* then cascades deletion to its parent.
|
|
27
|
+
*/
|
|
28
|
+
@Field(() => Boolean)
|
|
29
|
+
IsParentEntityDelete: boolean;
|
|
13
30
|
}
|
package/src/index.ts
CHANGED
|
@@ -91,6 +91,7 @@ export * from './resolvers/MCPResolver.js';
|
|
|
91
91
|
export * from './resolvers/ActionResolver.js';
|
|
92
92
|
export * from './resolvers/EntityCommunicationsResolver.js';
|
|
93
93
|
export * from './resolvers/EntityResolver.js';
|
|
94
|
+
export * from './resolvers/ISAEntityResolver.js';
|
|
94
95
|
export * from './resolvers/FileCategoryResolver.js';
|
|
95
96
|
export * from './resolvers/FileResolver.js';
|
|
96
97
|
export * from './resolvers/InfoResolver.js';
|
|
@@ -816,7 +816,9 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
816
816
|
// Provide default options if none provided
|
|
817
817
|
const deleteOptions = options || {
|
|
818
818
|
SkipEntityAIActions: false,
|
|
819
|
-
SkipEntityActions: false
|
|
819
|
+
SkipEntityActions: false,
|
|
820
|
+
ReplayOnly: false,
|
|
821
|
+
IsParentEntityDelete: false
|
|
820
822
|
};
|
|
821
823
|
|
|
822
824
|
// Use inherited DeleteRecord method from ResolverBase
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EntityInfo, IEntityDataProvider, Metadata, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { Arg, Ctx, Field, ObjectType, Query, Resolver } from 'type-graphql';
|
|
3
|
+
import { AppContext } from '../types.js';
|
|
4
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Result type for the IS-A child entity discovery query.
|
|
8
|
+
* Returns the name of the child entity type that has a record matching
|
|
9
|
+
* the given parent entity's primary key, or null if no child exists.
|
|
10
|
+
*/
|
|
11
|
+
@ObjectType()
|
|
12
|
+
export class ISAChildEntityResult {
|
|
13
|
+
@Field(() => Boolean)
|
|
14
|
+
Success: boolean;
|
|
15
|
+
|
|
16
|
+
@Field(() => String, { nullable: true })
|
|
17
|
+
ChildEntityName?: string;
|
|
18
|
+
|
|
19
|
+
@Field(() => String, { nullable: true })
|
|
20
|
+
ErrorMessage?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolver for IS-A entity hierarchy discovery.
|
|
25
|
+
*
|
|
26
|
+
* Provides a GraphQL endpoint for client-side code to discover child entity
|
|
27
|
+
* records in an IS-A hierarchy. This enables bidirectional chain construction
|
|
28
|
+
* where a loaded entity discovers its more-derived child type.
|
|
29
|
+
*/
|
|
30
|
+
@Resolver(ISAChildEntityResult)
|
|
31
|
+
export class ISAEntityResolver {
|
|
32
|
+
/**
|
|
33
|
+
* Discovers which IS-A child entity, if any, has a record with the given
|
|
34
|
+
* primary key value. The server executes a single UNION ALL query across
|
|
35
|
+
* all child entity tables for maximum efficiency.
|
|
36
|
+
*
|
|
37
|
+
* @param EntityName The parent entity name to check children for
|
|
38
|
+
* @param RecordID The primary key value to search for in child tables
|
|
39
|
+
* @returns The child entity name if found, or null with Success=true if no child exists
|
|
40
|
+
*/
|
|
41
|
+
@Query(() => ISAChildEntityResult)
|
|
42
|
+
async FindISAChildEntity(
|
|
43
|
+
@Arg('EntityName', () => String) EntityName: string,
|
|
44
|
+
@Arg('RecordID', () => String) RecordID: string,
|
|
45
|
+
@Ctx() { providers, userPayload }: AppContext
|
|
46
|
+
): Promise<ISAChildEntityResult> {
|
|
47
|
+
try {
|
|
48
|
+
const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
49
|
+
const md = new Metadata();
|
|
50
|
+
const entityInfo = md.Entities.find(e => e.Name === EntityName);
|
|
51
|
+
|
|
52
|
+
if (!entityInfo) {
|
|
53
|
+
return {
|
|
54
|
+
Success: false,
|
|
55
|
+
ErrorMessage: `Entity '${EntityName}' not found`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!entityInfo.IsParentType) {
|
|
60
|
+
return { Success: true };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Cast to IEntityDataProvider to access the optional FindISAChildEntity method
|
|
64
|
+
const entityProvider = provider as unknown as IEntityDataProvider;
|
|
65
|
+
if (!entityProvider.FindISAChildEntity) {
|
|
66
|
+
return {
|
|
67
|
+
Success: false,
|
|
68
|
+
ErrorMessage: 'Provider does not support FindISAChildEntity'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = await entityProvider.FindISAChildEntity(
|
|
73
|
+
entityInfo,
|
|
74
|
+
RecordID,
|
|
75
|
+
userPayload?.userRecord
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (result) {
|
|
79
|
+
return {
|
|
80
|
+
Success: true,
|
|
81
|
+
ChildEntityName: result.ChildEntityName
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { Success: true };
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return {
|
|
89
|
+
Success: false,
|
|
90
|
+
ErrorMessage: e instanceof Error ? e.message : String(e)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default ISAEntityResolver;
|