@memberjunction/server 5.0.0 → 5.2.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/README.md +2 -1
- package/dist/entitySubclasses/{entityPermissions.server.d.ts → MJEntityPermissionEntityServer.server.d.ts} +2 -2
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts.map +1 -0
- package/dist/entitySubclasses/{entityPermissions.server.js → MJEntityPermissionEntityServer.server.js} +9 -9
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js.map +1 -0
- package/dist/generated/generated.d.ts +213 -26
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +1183 -124
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +2 -2
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +28 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -0
- package/dist/resolvers/AdhocQueryResolver.js +140 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -0
- package/dist/resolvers/CreateQueryResolver.js +2 -2
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/services/TaskOrchestrator.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +52 -52
- package/src/__tests__/AdhocQueryResolver.test.ts +175 -0
- package/src/entitySubclasses/{entityPermissions.server.ts → MJEntityPermissionEntityServer.server.ts} +3 -3
- package/src/generated/generated.ts +827 -99
- package/src/generic/ResolverBase.ts +9 -9
- package/src/generic/RunViewResolver.ts +4 -4
- package/src/index.ts +1 -1
- package/src/resolvers/AdhocQueryResolver.ts +126 -0
- package/src/resolvers/CreateQueryResolver.ts +5 -5
- package/src/resolvers/RunAIAgentResolver.ts +4 -4
- package/src/resolvers/RunAIPromptResolver.ts +7 -7
- package/src/resolvers/RunTemplateResolver.ts +2 -2
- package/src/resolvers/UserViewResolver.ts +2 -2
- package/src/services/TaskOrchestrator.ts +5 -5
- package/src/types.ts +2 -2
- package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
- package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
- package/dist/apolloServer/TransactionPlugin.js +0 -46
- package/dist/apolloServer/TransactionPlugin.js.map +0 -1
- package/dist/auth/__tests__/backward-compatibility.test.d.ts +0 -2
- package/dist/auth/__tests__/backward-compatibility.test.d.ts.map +0 -1
- package/dist/auth/__tests__/backward-compatibility.test.js +0 -135
- package/dist/auth/__tests__/backward-compatibility.test.js.map +0 -1
- package/dist/entitySubclasses/entityPermissions.server.d.ts.map +0 -1
- package/dist/entitySubclasses/entityPermissions.server.js.map +0 -1
- package/dist/resolvers/AskSkipResolver.d.ts +0 -123
- package/dist/resolvers/AskSkipResolver.d.ts.map +0 -1
- package/dist/resolvers/AskSkipResolver.js +0 -1788
- package/dist/resolvers/AskSkipResolver.js.map +0 -1
- package/dist/scheduler/LearningCycleScheduler.d.ts +0 -4
- package/dist/scheduler/LearningCycleScheduler.d.ts.map +0 -1
- package/dist/scheduler/LearningCycleScheduler.js +0 -4
- package/dist/scheduler/LearningCycleScheduler.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.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",
|
|
@@ -26,57 +26,57 @@
|
|
|
26
26
|
"@apollo/server": "^4.9.1",
|
|
27
27
|
"@graphql-tools/schema": "latest",
|
|
28
28
|
"@graphql-tools/utils": "^11.0.0",
|
|
29
|
-
"@memberjunction/actions": "5.
|
|
30
|
-
"@memberjunction/actions-base": "5.
|
|
31
|
-
"@memberjunction/actions-apollo": "5.
|
|
32
|
-
"@memberjunction/actions-bizapps-accounting": "5.
|
|
33
|
-
"@memberjunction/actions-bizapps-crm": "5.
|
|
34
|
-
"@memberjunction/actions-bizapps-formbuilders": "5.
|
|
35
|
-
"@memberjunction/actions-bizapps-lms": "5.
|
|
36
|
-
"@memberjunction/actions-bizapps-social": "5.
|
|
37
|
-
"@memberjunction/ai": "5.
|
|
38
|
-
"@memberjunction/ai-mcp-client": "5.
|
|
39
|
-
"@memberjunction/ai-agent-manager": "5.
|
|
40
|
-
"@memberjunction/ai-agent-manager-actions": "5.
|
|
41
|
-
"@memberjunction/ai-agents": "5.
|
|
42
|
-
"@memberjunction/ai-core-plus": "5.
|
|
43
|
-
"@memberjunction/ai-prompts": "5.
|
|
44
|
-
"@memberjunction/ai-provider-bundle": "5.
|
|
45
|
-
"@memberjunction/ai-vectors-pinecone": "5.
|
|
46
|
-
"@memberjunction/aiengine": "5.
|
|
47
|
-
"@memberjunction/communication-ms-graph": "5.
|
|
48
|
-
"@memberjunction/communication-sendgrid": "5.
|
|
49
|
-
"@memberjunction/communication-types": "5.
|
|
50
|
-
"@memberjunction/component-registry-client-sdk": "5.
|
|
51
|
-
"@memberjunction/config": "5.
|
|
52
|
-
"@memberjunction/core": "5.
|
|
53
|
-
"@memberjunction/core-actions": "5.
|
|
54
|
-
"@memberjunction/core-entities": "5.
|
|
55
|
-
"@memberjunction/core-entities-server": "5.
|
|
56
|
-
"@memberjunction/data-context": "5.
|
|
57
|
-
"@memberjunction/data-context-server": "5.
|
|
58
|
-
"@memberjunction/doc-utils": "5.
|
|
59
|
-
"@memberjunction/api-keys": "5.
|
|
60
|
-
"@memberjunction/encryption": "5.
|
|
61
|
-
"@memberjunction/entity-communications-base": "5.
|
|
62
|
-
"@memberjunction/entity-communications-server": "5.
|
|
63
|
-
"@memberjunction/external-change-detection": "5.
|
|
64
|
-
"@memberjunction/global": "5.
|
|
65
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
66
|
-
"@memberjunction/interactive-component-types": "5.
|
|
67
|
-
"@memberjunction/notifications": "5.
|
|
68
|
-
"@memberjunction/queue": "5.
|
|
69
|
-
"@memberjunction/scheduling-actions": "5.
|
|
70
|
-
"@memberjunction/scheduling-base-types": "5.
|
|
71
|
-
"@memberjunction/scheduling-engine": "5.
|
|
72
|
-
"@memberjunction/scheduling-engine-base": "5.
|
|
73
|
-
"@memberjunction/skip-types": "5.
|
|
74
|
-
"@memberjunction/sqlserver-dataprovider": "5.
|
|
75
|
-
"@memberjunction/storage": "5.
|
|
76
|
-
"@memberjunction/templates": "5.
|
|
77
|
-
"@memberjunction/testing-engine": "5.
|
|
78
|
-
"@memberjunction/testing-engine-base": "5.
|
|
79
|
-
"@memberjunction/version-history": "5.
|
|
29
|
+
"@memberjunction/actions": "5.2.0",
|
|
30
|
+
"@memberjunction/actions-base": "5.2.0",
|
|
31
|
+
"@memberjunction/actions-apollo": "5.2.0",
|
|
32
|
+
"@memberjunction/actions-bizapps-accounting": "5.2.0",
|
|
33
|
+
"@memberjunction/actions-bizapps-crm": "5.2.0",
|
|
34
|
+
"@memberjunction/actions-bizapps-formbuilders": "5.2.0",
|
|
35
|
+
"@memberjunction/actions-bizapps-lms": "5.2.0",
|
|
36
|
+
"@memberjunction/actions-bizapps-social": "5.2.0",
|
|
37
|
+
"@memberjunction/ai": "5.2.0",
|
|
38
|
+
"@memberjunction/ai-mcp-client": "5.2.0",
|
|
39
|
+
"@memberjunction/ai-agent-manager": "5.2.0",
|
|
40
|
+
"@memberjunction/ai-agent-manager-actions": "5.2.0",
|
|
41
|
+
"@memberjunction/ai-agents": "5.2.0",
|
|
42
|
+
"@memberjunction/ai-core-plus": "5.2.0",
|
|
43
|
+
"@memberjunction/ai-prompts": "5.2.0",
|
|
44
|
+
"@memberjunction/ai-provider-bundle": "5.2.0",
|
|
45
|
+
"@memberjunction/ai-vectors-pinecone": "5.2.0",
|
|
46
|
+
"@memberjunction/aiengine": "5.2.0",
|
|
47
|
+
"@memberjunction/communication-ms-graph": "5.2.0",
|
|
48
|
+
"@memberjunction/communication-sendgrid": "5.2.0",
|
|
49
|
+
"@memberjunction/communication-types": "5.2.0",
|
|
50
|
+
"@memberjunction/component-registry-client-sdk": "5.2.0",
|
|
51
|
+
"@memberjunction/config": "5.2.0",
|
|
52
|
+
"@memberjunction/core": "5.2.0",
|
|
53
|
+
"@memberjunction/core-actions": "5.2.0",
|
|
54
|
+
"@memberjunction/core-entities": "5.2.0",
|
|
55
|
+
"@memberjunction/core-entities-server": "5.2.0",
|
|
56
|
+
"@memberjunction/data-context": "5.2.0",
|
|
57
|
+
"@memberjunction/data-context-server": "5.2.0",
|
|
58
|
+
"@memberjunction/doc-utils": "5.2.0",
|
|
59
|
+
"@memberjunction/api-keys": "5.2.0",
|
|
60
|
+
"@memberjunction/encryption": "5.2.0",
|
|
61
|
+
"@memberjunction/entity-communications-base": "5.2.0",
|
|
62
|
+
"@memberjunction/entity-communications-server": "5.2.0",
|
|
63
|
+
"@memberjunction/external-change-detection": "5.2.0",
|
|
64
|
+
"@memberjunction/global": "5.2.0",
|
|
65
|
+
"@memberjunction/graphql-dataprovider": "5.2.0",
|
|
66
|
+
"@memberjunction/interactive-component-types": "5.2.0",
|
|
67
|
+
"@memberjunction/notifications": "5.2.0",
|
|
68
|
+
"@memberjunction/queue": "5.2.0",
|
|
69
|
+
"@memberjunction/scheduling-actions": "5.2.0",
|
|
70
|
+
"@memberjunction/scheduling-base-types": "5.2.0",
|
|
71
|
+
"@memberjunction/scheduling-engine": "5.2.0",
|
|
72
|
+
"@memberjunction/scheduling-engine-base": "5.2.0",
|
|
73
|
+
"@memberjunction/skip-types": "5.2.0",
|
|
74
|
+
"@memberjunction/sqlserver-dataprovider": "5.2.0",
|
|
75
|
+
"@memberjunction/storage": "5.2.0",
|
|
76
|
+
"@memberjunction/templates": "5.2.0",
|
|
77
|
+
"@memberjunction/testing-engine": "5.2.0",
|
|
78
|
+
"@memberjunction/testing-engine-base": "5.2.0",
|
|
79
|
+
"@memberjunction/version-history": "5.2.0",
|
|
80
80
|
"@types/compression": "^1.8.1",
|
|
81
81
|
"@types/cors": "^2.8.19",
|
|
82
82
|
"@types/jsonwebtoken": "9.0.10",
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { SQLExpressionValidator } from '@memberjunction/global';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests for AdhocQueryResolver.
|
|
6
|
+
*
|
|
7
|
+
* Since the resolver has heavy dependencies (type-graphql decorators, mssql, AppContext),
|
|
8
|
+
* we test the core logic through the SQLExpressionValidator integration and
|
|
9
|
+
* verify the resolver's structure and error-handling contracts.
|
|
10
|
+
*/
|
|
11
|
+
describe('AdhocQueryResolver', () => {
|
|
12
|
+
let validator: SQLExpressionValidator;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
validator = SQLExpressionValidator.Instance;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('SQL validation gate', () => {
|
|
19
|
+
it('should accept valid SELECT query', () => {
|
|
20
|
+
const result = validator.validateFullQuery('SELECT TOP 10 * FROM __mj.vwUsers');
|
|
21
|
+
expect(result.valid).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should accept valid CTE query', () => {
|
|
25
|
+
const result = validator.validateFullQuery(`
|
|
26
|
+
WITH cte AS (SELECT ID, Name FROM __mj.vwUsers)
|
|
27
|
+
SELECT * FROM cte
|
|
28
|
+
`);
|
|
29
|
+
expect(result.valid).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should reject INSERT statement', () => {
|
|
33
|
+
const result = validator.validateFullQuery("INSERT INTO Users (Name) VALUES ('hacked')");
|
|
34
|
+
expect(result.valid).toBe(false);
|
|
35
|
+
expect(result.error).toContain('INSERT');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should reject DELETE statement', () => {
|
|
39
|
+
const result = validator.validateFullQuery("DELETE FROM Users WHERE 1=1");
|
|
40
|
+
expect(result.valid).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should reject UPDATE statement', () => {
|
|
44
|
+
const result = validator.validateFullQuery("UPDATE Users SET IsAdmin = 1");
|
|
45
|
+
expect(result.valid).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should reject DROP TABLE', () => {
|
|
49
|
+
const result = validator.validateFullQuery("DROP TABLE Users");
|
|
50
|
+
expect(result.valid).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should reject EXEC', () => {
|
|
54
|
+
const result = validator.validateFullQuery("EXEC sp_executesql N'SELECT 1'");
|
|
55
|
+
expect(result.valid).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should reject multi-statement injection via semicolon', () => {
|
|
59
|
+
const result = validator.validateFullQuery("SELECT 1; DROP TABLE Users");
|
|
60
|
+
expect(result.valid).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should reject empty SQL', () => {
|
|
64
|
+
const result = validator.validateFullQuery('');
|
|
65
|
+
expect(result.valid).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('buildErrorResult contract', () => {
|
|
70
|
+
it('should return the expected error shape', () => {
|
|
71
|
+
// This tests the contract that the resolver's buildErrorResult must fulfill
|
|
72
|
+
const expectedShape = {
|
|
73
|
+
QueryID: '',
|
|
74
|
+
QueryName: 'Ad-Hoc Query',
|
|
75
|
+
Success: false,
|
|
76
|
+
Results: '[]',
|
|
77
|
+
RowCount: 0,
|
|
78
|
+
TotalRowCount: 0,
|
|
79
|
+
ExecutionTime: 0,
|
|
80
|
+
ErrorMessage: 'SQL validation failed'
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
expect(expectedShape.Success).toBe(false);
|
|
84
|
+
expect(expectedShape.QueryID).toBe('');
|
|
85
|
+
expect(expectedShape.QueryName).toBe('Ad-Hoc Query');
|
|
86
|
+
expect(expectedShape.Results).toBe('[]');
|
|
87
|
+
expect(expectedShape.ErrorMessage).toBe('SQL validation failed');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('success result contract', () => {
|
|
92
|
+
it('should return the expected success shape', () => {
|
|
93
|
+
// This tests the contract that the resolver's success path must fulfill
|
|
94
|
+
const mockRecordset = [{ ID: '1', Name: 'Test' }, { ID: '2', Name: 'Other' }];
|
|
95
|
+
const expectedShape = {
|
|
96
|
+
QueryID: '',
|
|
97
|
+
QueryName: 'Ad-Hoc Query',
|
|
98
|
+
Success: true,
|
|
99
|
+
Results: JSON.stringify(mockRecordset),
|
|
100
|
+
RowCount: mockRecordset.length,
|
|
101
|
+
TotalRowCount: mockRecordset.length,
|
|
102
|
+
ExecutionTime: 42,
|
|
103
|
+
ErrorMessage: ''
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
expect(expectedShape.Success).toBe(true);
|
|
107
|
+
expect(expectedShape.QueryID).toBe('');
|
|
108
|
+
expect(expectedShape.QueryName).toBe('Ad-Hoc Query');
|
|
109
|
+
expect(JSON.parse(expectedShape.Results)).toHaveLength(2);
|
|
110
|
+
expect(expectedShape.RowCount).toBe(2);
|
|
111
|
+
expect(expectedShape.ErrorMessage).toBe('');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('timeout contract', () => {
|
|
116
|
+
it('should default to 30 seconds when TimeoutSeconds is not provided', () => {
|
|
117
|
+
const defaultTimeout = undefined ?? 30;
|
|
118
|
+
expect(defaultTimeout).toBe(30);
|
|
119
|
+
expect(defaultTimeout * 1000).toBe(30000);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should use provided TimeoutSeconds', () => {
|
|
123
|
+
const customTimeout = 60 ?? 30;
|
|
124
|
+
expect(customTimeout).toBe(60);
|
|
125
|
+
expect(customTimeout * 1000).toBe(60000);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should produce correct error message for timeout', () => {
|
|
129
|
+
const timeoutSeconds = 30;
|
|
130
|
+
const errorMessage = `Query execution exceeded ${timeoutSeconds} second timeout`;
|
|
131
|
+
expect(errorMessage).toContain('30');
|
|
132
|
+
expect(errorMessage).toContain('timeout');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('complex query validation', () => {
|
|
137
|
+
it('should accept query with JOINs and subqueries', () => {
|
|
138
|
+
const sql = `
|
|
139
|
+
SELECT a.Name, COUNT(r.ID) AS TotalRuns
|
|
140
|
+
FROM __mj.vwAIAgents a
|
|
141
|
+
INNER JOIN __mj.vwAIAgentRuns r ON r.AgentID = a.ID
|
|
142
|
+
WHERE EXISTS (SELECT 1 FROM __mj.vwUsers u WHERE u.ID = a.CreatedByUserID)
|
|
143
|
+
GROUP BY a.Name
|
|
144
|
+
ORDER BY TotalRuns DESC
|
|
145
|
+
`;
|
|
146
|
+
const result = validator.validateFullQuery(sql);
|
|
147
|
+
expect(result.valid).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should accept query with UNION', () => {
|
|
151
|
+
const sql = `
|
|
152
|
+
SELECT Name, 'Agent' AS Type FROM __mj.vwAIAgents
|
|
153
|
+
UNION ALL
|
|
154
|
+
SELECT Name, 'Model' AS Type FROM __mj.vwAIModels
|
|
155
|
+
`;
|
|
156
|
+
const result = validator.validateFullQuery(sql);
|
|
157
|
+
expect(result.valid).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should accept query with comments (agent-generated)', () => {
|
|
161
|
+
const sql = `
|
|
162
|
+
-- ============================================================
|
|
163
|
+
-- Agent Performance Report
|
|
164
|
+
-- ============================================================
|
|
165
|
+
SELECT TOP 100 a.Name, COUNT(*) AS RunCount
|
|
166
|
+
FROM __mj.vwAIAgents a
|
|
167
|
+
INNER JOIN __mj.vwAIAgentRuns r ON r.AgentID = a.ID
|
|
168
|
+
GROUP BY a.Name
|
|
169
|
+
ORDER BY RunCount DESC
|
|
170
|
+
`;
|
|
171
|
+
const result = validator.validateFullQuery(sql);
|
|
172
|
+
expect(result.valid).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -12,7 +12,7 @@ import { ___codeGenAPIPort, ___codeGenAPISubmissionDelay, ___codeGenAPIURL } fro
|
|
|
12
12
|
* happens in the server. That's why it is not in the core-entities-server package.
|
|
13
13
|
*/
|
|
14
14
|
@RegisterClass(BaseEntity, 'MJ: Entity Permissions')
|
|
15
|
-
export class
|
|
15
|
+
export class MJEntityPermissionEntityServer extends MJEntityPermissionEntity {
|
|
16
16
|
protected static _entityIDQueue: string[] = [];
|
|
17
17
|
protected static _lastModifiedTime: Date | null = null;
|
|
18
18
|
protected static _submissionTimer: NodeJS.Timeout | null = null;
|
|
@@ -89,7 +89,7 @@ export class EntityPermissionsEntity_Server extends MJEntityPermissionEntity {
|
|
|
89
89
|
|
|
90
90
|
override Save(options?: EntitySaveOptions): Promise<boolean> {
|
|
91
91
|
// simply queue up the entity ID
|
|
92
|
-
if (this.Dirty || options?.IgnoreDirtyState)
|
|
92
|
+
if (this.Dirty || options?.IgnoreDirtyState) MJEntityPermissionEntityServer.AddToQueue(this.EntityID);
|
|
93
93
|
|
|
94
94
|
return super.Save(options);
|
|
95
95
|
}
|
|
@@ -98,7 +98,7 @@ export class EntityPermissionsEntity_Server extends MJEntityPermissionEntity {
|
|
|
98
98
|
const success = await super.Delete(options);
|
|
99
99
|
|
|
100
100
|
// simply queue up the entity ID if the delete worked
|
|
101
|
-
if (success)
|
|
101
|
+
if (success) MJEntityPermissionEntityServer.AddToQueue(this.EntityID);
|
|
102
102
|
|
|
103
103
|
return success;
|
|
104
104
|
}
|