@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.
Files changed (62) hide show
  1. package/README.md +2 -1
  2. package/dist/entitySubclasses/{entityPermissions.server.d.ts → MJEntityPermissionEntityServer.server.d.ts} +2 -2
  3. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts.map +1 -0
  4. package/dist/entitySubclasses/{entityPermissions.server.js → MJEntityPermissionEntityServer.server.js} +9 -9
  5. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js.map +1 -0
  6. package/dist/generated/generated.d.ts +213 -26
  7. package/dist/generated/generated.d.ts.map +1 -1
  8. package/dist/generated/generated.js +1183 -124
  9. package/dist/generated/generated.js.map +1 -1
  10. package/dist/generic/ResolverBase.d.ts +2 -2
  11. package/dist/generic/ResolverBase.d.ts.map +1 -1
  12. package/dist/generic/ResolverBase.js.map +1 -1
  13. package/dist/generic/RunViewResolver.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/resolvers/AdhocQueryResolver.d.ts +28 -0
  19. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -0
  20. package/dist/resolvers/AdhocQueryResolver.js +140 -0
  21. package/dist/resolvers/AdhocQueryResolver.js.map +1 -0
  22. package/dist/resolvers/CreateQueryResolver.js +2 -2
  23. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  24. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  25. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  26. package/dist/resolvers/UserViewResolver.js.map +1 -1
  27. package/dist/services/TaskOrchestrator.js.map +1 -1
  28. package/dist/types.d.ts +2 -2
  29. package/dist/types.d.ts.map +1 -1
  30. package/package.json +52 -52
  31. package/src/__tests__/AdhocQueryResolver.test.ts +175 -0
  32. package/src/entitySubclasses/{entityPermissions.server.ts → MJEntityPermissionEntityServer.server.ts} +3 -3
  33. package/src/generated/generated.ts +827 -99
  34. package/src/generic/ResolverBase.ts +9 -9
  35. package/src/generic/RunViewResolver.ts +4 -4
  36. package/src/index.ts +1 -1
  37. package/src/resolvers/AdhocQueryResolver.ts +126 -0
  38. package/src/resolvers/CreateQueryResolver.ts +5 -5
  39. package/src/resolvers/RunAIAgentResolver.ts +4 -4
  40. package/src/resolvers/RunAIPromptResolver.ts +7 -7
  41. package/src/resolvers/RunTemplateResolver.ts +2 -2
  42. package/src/resolvers/UserViewResolver.ts +2 -2
  43. package/src/services/TaskOrchestrator.ts +5 -5
  44. package/src/types.ts +2 -2
  45. package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
  46. package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
  47. package/dist/apolloServer/TransactionPlugin.js +0 -46
  48. package/dist/apolloServer/TransactionPlugin.js.map +0 -1
  49. package/dist/auth/__tests__/backward-compatibility.test.d.ts +0 -2
  50. package/dist/auth/__tests__/backward-compatibility.test.d.ts.map +0 -1
  51. package/dist/auth/__tests__/backward-compatibility.test.js +0 -135
  52. package/dist/auth/__tests__/backward-compatibility.test.js.map +0 -1
  53. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +0 -1
  54. package/dist/entitySubclasses/entityPermissions.server.js.map +0 -1
  55. package/dist/resolvers/AskSkipResolver.d.ts +0 -123
  56. package/dist/resolvers/AskSkipResolver.d.ts.map +0 -1
  57. package/dist/resolvers/AskSkipResolver.js +0 -1788
  58. package/dist/resolvers/AskSkipResolver.js.map +0 -1
  59. package/dist/scheduler/LearningCycleScheduler.d.ts +0 -4
  60. package/dist/scheduler/LearningCycleScheduler.d.ts.map +0 -1
  61. package/dist/scheduler/LearningCycleScheduler.js +0 -4
  62. 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.0.0",
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.0.0",
30
- "@memberjunction/actions-base": "5.0.0",
31
- "@memberjunction/actions-apollo": "5.0.0",
32
- "@memberjunction/actions-bizapps-accounting": "5.0.0",
33
- "@memberjunction/actions-bizapps-crm": "5.0.0",
34
- "@memberjunction/actions-bizapps-formbuilders": "5.0.0",
35
- "@memberjunction/actions-bizapps-lms": "5.0.0",
36
- "@memberjunction/actions-bizapps-social": "5.0.0",
37
- "@memberjunction/ai": "5.0.0",
38
- "@memberjunction/ai-mcp-client": "5.0.0",
39
- "@memberjunction/ai-agent-manager": "5.0.0",
40
- "@memberjunction/ai-agent-manager-actions": "5.0.0",
41
- "@memberjunction/ai-agents": "5.0.0",
42
- "@memberjunction/ai-core-plus": "5.0.0",
43
- "@memberjunction/ai-prompts": "5.0.0",
44
- "@memberjunction/ai-provider-bundle": "5.0.0",
45
- "@memberjunction/ai-vectors-pinecone": "5.0.0",
46
- "@memberjunction/aiengine": "5.0.0",
47
- "@memberjunction/communication-ms-graph": "5.0.0",
48
- "@memberjunction/communication-sendgrid": "5.0.0",
49
- "@memberjunction/communication-types": "5.0.0",
50
- "@memberjunction/component-registry-client-sdk": "5.0.0",
51
- "@memberjunction/config": "5.0.0",
52
- "@memberjunction/core": "5.0.0",
53
- "@memberjunction/core-actions": "5.0.0",
54
- "@memberjunction/core-entities": "5.0.0",
55
- "@memberjunction/core-entities-server": "5.0.0",
56
- "@memberjunction/data-context": "5.0.0",
57
- "@memberjunction/data-context-server": "5.0.0",
58
- "@memberjunction/doc-utils": "5.0.0",
59
- "@memberjunction/api-keys": "5.0.0",
60
- "@memberjunction/encryption": "5.0.0",
61
- "@memberjunction/entity-communications-base": "5.0.0",
62
- "@memberjunction/entity-communications-server": "5.0.0",
63
- "@memberjunction/external-change-detection": "5.0.0",
64
- "@memberjunction/global": "5.0.0",
65
- "@memberjunction/graphql-dataprovider": "5.0.0",
66
- "@memberjunction/interactive-component-types": "5.0.0",
67
- "@memberjunction/notifications": "5.0.0",
68
- "@memberjunction/queue": "5.0.0",
69
- "@memberjunction/scheduling-actions": "5.0.0",
70
- "@memberjunction/scheduling-base-types": "5.0.0",
71
- "@memberjunction/scheduling-engine": "5.0.0",
72
- "@memberjunction/scheduling-engine-base": "5.0.0",
73
- "@memberjunction/skip-types": "5.0.0",
74
- "@memberjunction/sqlserver-dataprovider": "5.0.0",
75
- "@memberjunction/storage": "5.0.0",
76
- "@memberjunction/templates": "5.0.0",
77
- "@memberjunction/testing-engine": "5.0.0",
78
- "@memberjunction/testing-engine-base": "5.0.0",
79
- "@memberjunction/version-history": "5.0.0",
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 EntityPermissionsEntity_Server extends MJEntityPermissionEntity {
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) EntityPermissionsEntity_Server.AddToQueue(this.EntityID);
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) EntityPermissionsEntity_Server.AddToQueue(this.EntityID);
101
+ if (success) MJEntityPermissionEntityServer.AddToQueue(this.EntityID);
102
102
 
103
103
  return success;
104
104
  }