@memberjunction/server 2.47.0 → 2.49.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/apolloServer/TransactionPlugin.js +7 -7
- package/dist/apolloServer/TransactionPlugin.js.map +1 -1
- package/dist/auth/index.d.ts +3 -3
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +2 -13
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/context.d.ts +3 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +686 -329
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +5786 -3286
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +17 -17
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +7 -5
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +12 -4
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -14
- package/dist/index.js.map +1 -1
- package/dist/orm.d.ts +3 -3
- package/dist/orm.d.ts.map +1 -1
- package/dist/orm.js +14 -15
- package/dist/orm.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +19 -19
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +17 -12
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/EntityResolver.d.ts +2 -1
- package/dist/resolvers/EntityResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityResolver.js +6 -3
- package/dist/resolvers/EntityResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +10 -2
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +4 -2
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/ReportResolver.d.ts.map +1 -1
- package/dist/resolvers/ReportResolver.js +5 -3
- package/dist/resolvers/ReportResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +19 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -0
- package/dist/resolvers/RunAIAgentResolver.js +194 -0
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -0
- package/dist/resolvers/UserFavoriteResolver.d.ts +2 -2
- package/dist/resolvers/UserViewResolver.d.ts +4 -4
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/types.d.ts +6 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +10 -3
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +22 -3
- package/dist/util.js.map +1 -1
- package/package.json +24 -25
- package/src/apolloServer/TransactionPlugin.ts +8 -8
- package/src/auth/index.ts +4 -4
- package/src/auth/newUsers.ts +0 -1
- package/src/context.ts +2 -2
- package/src/generated/generated.ts +4759 -3021
- package/src/generic/ResolverBase.ts +23 -22
- package/src/generic/RunViewResolver.ts +12 -4
- package/src/index.ts +18 -15
- package/src/orm.ts +18 -17
- package/src/resolvers/AskSkipResolver.ts +36 -32
- package/src/resolvers/EntityResolver.ts +5 -2
- package/src/resolvers/FileCategoryResolver.ts +9 -2
- package/src/resolvers/GetDataResolver.ts +4 -2
- package/src/resolvers/ReportResolver.ts +5 -3
- package/src/resolvers/RunAIAgentResolver.ts +174 -0
- package/src/resolvers/UserViewResolver.ts +0 -1
- package/src/types.ts +8 -8
- package/src/util.ts +31 -6
|
@@ -17,7 +17,7 @@ import { AuditLogEntity, UserViewEntity } from '@memberjunction/core-entities';
|
|
|
17
17
|
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
18
18
|
import { PubSubEngine } from 'type-graphql';
|
|
19
19
|
import { GraphQLError } from 'graphql';
|
|
20
|
-
import
|
|
20
|
+
import sql from 'mssql';
|
|
21
21
|
import { httpTransport, CloudEvent, emitterFor } from 'cloudevents';
|
|
22
22
|
|
|
23
23
|
import { RunViewGenericParams, UserPayload } from '../types.js';
|
|
@@ -70,7 +70,7 @@ export class ResolverBase {
|
|
|
70
70
|
return dataObject;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
protected ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: []) {
|
|
73
|
+
protected ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: any[]) {
|
|
74
74
|
// iterate through the array and call MapFieldNamesToCodeNames for each element
|
|
75
75
|
if (dataObjectArray && dataObjectArray.length > 0) {
|
|
76
76
|
dataObjectArray.forEach((element) => {
|
|
@@ -80,29 +80,30 @@ export class ResolverBase {
|
|
|
80
80
|
return dataObjectArray;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
protected async findBy(dataSource:
|
|
83
|
+
protected async findBy(dataSource: sql.ConnectionPool, entity: string, params: any) {
|
|
84
84
|
// build the SQL query based on the params passed in
|
|
85
85
|
const md = new Metadata();
|
|
86
86
|
const e = md.Entities.find((e) => e.Name === entity);
|
|
87
87
|
if (!e) throw new Error(`Entity ${entity} not found in metadata`);
|
|
88
88
|
// now build a SQL string using the entityInfo and using the properties in the params object
|
|
89
|
-
let
|
|
89
|
+
let sqlQuery = `SELECT * FROM ${e.SchemaName}.${e.BaseView} WHERE `;
|
|
90
90
|
const keys = Object.keys(params);
|
|
91
91
|
keys.forEach((k, i) => {
|
|
92
|
-
if (i > 0)
|
|
92
|
+
if (i > 0) sqlQuery += ' AND ';
|
|
93
93
|
// look up the field in the entityInfo to see if it needs quotes
|
|
94
94
|
const field = e.Fields.find((f) => f.Name === k);
|
|
95
95
|
if (!field) throw new Error(`Field ${k} not found in entity ${entity}`);
|
|
96
96
|
const quotes = field.NeedsQuotes ? "'" : '';
|
|
97
|
-
|
|
97
|
+
sqlQuery += `${k} = ${quotes}${params[k]}${quotes}`;
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
// ok, now we have a SQL string, run it and return the results
|
|
101
|
-
const
|
|
102
|
-
|
|
101
|
+
const request = new sql.Request(dataSource);
|
|
102
|
+
const result = await request.query(sqlQuery);
|
|
103
|
+
return result.recordset;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
async RunViewByNameGeneric(viewInput: RunViewByNameInput, dataSource:
|
|
106
|
+
async RunViewByNameGeneric(viewInput: RunViewByNameInput, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
106
107
|
try {
|
|
107
108
|
const viewInfo: UserViewEntity = this.safeFirstArrayElement(
|
|
108
109
|
await this.findBy(dataSource, 'User Views', { Name: viewInput.ViewName })
|
|
@@ -131,7 +132,7 @@ export class ResolverBase {
|
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
async RunViewByIDGeneric(viewInput: RunViewByIDInput, dataSource:
|
|
135
|
+
async RunViewByIDGeneric(viewInput: RunViewByIDInput, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
135
136
|
try {
|
|
136
137
|
const viewInfo: UserViewEntity = this.safeFirstArrayElement(await this.findBy(dataSource, 'User Views', { ID: viewInput.ViewID }));
|
|
137
138
|
return this.RunViewGenericInternal(
|
|
@@ -158,7 +159,7 @@ export class ResolverBase {
|
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
async RunDynamicViewGeneric(viewInput: RunDynamicViewInput, dataSource:
|
|
162
|
+
async RunDynamicViewGeneric(viewInput: RunDynamicViewInput, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
162
163
|
try {
|
|
163
164
|
const md = new Metadata();
|
|
164
165
|
const entity = md.Entities.find((e) => e.Name === viewInput.EntityName);
|
|
@@ -197,7 +198,7 @@ export class ResolverBase {
|
|
|
197
198
|
|
|
198
199
|
async RunViewsGeneric(
|
|
199
200
|
viewInputs: (RunViewByNameInput & RunViewByIDInput & RunDynamicViewInput)[],
|
|
200
|
-
dataSource:
|
|
201
|
+
dataSource: sql.ConnectionPool,
|
|
201
202
|
userPayload: UserPayload,
|
|
202
203
|
pubSub: PubSubEngine
|
|
203
204
|
) {
|
|
@@ -332,7 +333,7 @@ export class ResolverBase {
|
|
|
332
333
|
*/
|
|
333
334
|
protected async RunViewGenericInternal(
|
|
334
335
|
viewInfo: UserViewEntity,
|
|
335
|
-
dataSource:
|
|
336
|
+
dataSource: sql.ConnectionPool,
|
|
336
337
|
extraFilter: string,
|
|
337
338
|
orderBy: string,
|
|
338
339
|
userSearchString: string,
|
|
@@ -660,7 +661,7 @@ export class ResolverBase {
|
|
|
660
661
|
}
|
|
661
662
|
}
|
|
662
663
|
|
|
663
|
-
protected async CreateRecord(entityName: string, input: any, dataSource:
|
|
664
|
+
protected async CreateRecord(entityName: string, input: any, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
664
665
|
if (await this.BeforeCreate(dataSource, input)) {
|
|
665
666
|
// fire event and proceed if it wasn't cancelled
|
|
666
667
|
const entityObject = await new Metadata().GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
|
|
@@ -680,12 +681,12 @@ export class ResolverBase {
|
|
|
680
681
|
}
|
|
681
682
|
|
|
682
683
|
// Before/After CREATE Event Hooks for Sub-Classes to Override
|
|
683
|
-
protected async BeforeCreate(dataSource:
|
|
684
|
+
protected async BeforeCreate(dataSource: sql.ConnectionPool, input: any): Promise<boolean> {
|
|
684
685
|
return true;
|
|
685
686
|
}
|
|
686
|
-
protected async AfterCreate(dataSource:
|
|
687
|
+
protected async AfterCreate(dataSource: sql.ConnectionPool, input: any) {}
|
|
687
688
|
|
|
688
|
-
protected async UpdateRecord(entityName: string, input: any, dataSource:
|
|
689
|
+
protected async UpdateRecord(entityName: string, input: any, dataSource: sql.ConnectionPool, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
689
690
|
if (await this.BeforeUpdate(dataSource, input)) {
|
|
690
691
|
// fire event and proceed if it wasn't cancelled
|
|
691
692
|
const md = new Metadata();
|
|
@@ -893,7 +894,7 @@ export class ResolverBase {
|
|
|
893
894
|
entityName: string,
|
|
894
895
|
key: CompositeKey,
|
|
895
896
|
options: DeleteOptionsInput,
|
|
896
|
-
dataSource:
|
|
897
|
+
dataSource: sql.ConnectionPool,
|
|
897
898
|
userPayload: UserPayload,
|
|
898
899
|
pubSub: PubSubEngine
|
|
899
900
|
) {
|
|
@@ -921,14 +922,14 @@ export class ResolverBase {
|
|
|
921
922
|
}
|
|
922
923
|
|
|
923
924
|
// Before/After DELETE Event Hooks for Sub-Classes to Override
|
|
924
|
-
protected async BeforeDelete(dataSource:
|
|
925
|
+
protected async BeforeDelete(dataSource: sql.ConnectionPool, key: CompositeKey): Promise<boolean> {
|
|
925
926
|
return true;
|
|
926
927
|
}
|
|
927
|
-
protected async AfterDelete(dataSource:
|
|
928
|
+
protected async AfterDelete(dataSource: sql.ConnectionPool, key: CompositeKey) {}
|
|
928
929
|
|
|
929
930
|
// Before/After UPDATE Event Hooks for Sub-Classes to Override
|
|
930
|
-
protected async BeforeUpdate(dataSource:
|
|
931
|
+
protected async BeforeUpdate(dataSource: sql.ConnectionPool, input: any): Promise<boolean> {
|
|
931
932
|
return true;
|
|
932
933
|
}
|
|
933
|
-
protected async AfterUpdate(dataSource:
|
|
934
|
+
protected async AfterUpdate(dataSource: sql.ConnectionPool, input: any) {}
|
|
934
935
|
}
|
|
@@ -463,7 +463,9 @@ export class RunViewResolver extends ResolverBase {
|
|
|
463
463
|
const rawData = await super.RunViewByNameGeneric(input, dataSource, userPayload, pubSub);
|
|
464
464
|
if (rawData === null) return null;
|
|
465
465
|
|
|
466
|
-
const
|
|
466
|
+
const request = dataSource.request();
|
|
467
|
+
const entityIdResult = await request.query(`SELECT EntityID from [${this.MJCoreSchema}].vwUserViews WHERE Name='${input.ViewName}'`);
|
|
468
|
+
const entityId = entityIdResult.recordset;
|
|
467
469
|
const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
|
|
468
470
|
return {
|
|
469
471
|
Results: returnData,
|
|
@@ -488,7 +490,9 @@ export class RunViewResolver extends ResolverBase {
|
|
|
488
490
|
const rawData = await super.RunViewByIDGeneric(input, dataSource, userPayload, pubSub);
|
|
489
491
|
if (rawData === null) return null;
|
|
490
492
|
|
|
491
|
-
const
|
|
493
|
+
const request = dataSource.request();
|
|
494
|
+
const entityIdResult = await request.query(`SELECT EntityID from [${this.MJCoreSchema}].vwUserViews WHERE ID=${input.ViewID}`);
|
|
495
|
+
const entityId = entityIdResult.recordset;
|
|
492
496
|
const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
|
|
493
497
|
return {
|
|
494
498
|
Results: returnData,
|
|
@@ -513,7 +517,9 @@ export class RunViewResolver extends ResolverBase {
|
|
|
513
517
|
const rawData = await super.RunDynamicViewGeneric(input, dataSource, userPayload, pubSub);
|
|
514
518
|
if (rawData === null) return null;
|
|
515
519
|
|
|
516
|
-
const
|
|
520
|
+
const request = dataSource.request();
|
|
521
|
+
const entityIdResult = await request.query(`SELECT ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input.EntityName}'`);
|
|
522
|
+
const entityId = entityIdResult.recordset;
|
|
517
523
|
const returnData = this.processRawData(rawData.Results, entityId[0].EntityID);
|
|
518
524
|
return {
|
|
519
525
|
Results: returnData,
|
|
@@ -542,9 +548,11 @@ export class RunViewResolver extends ResolverBase {
|
|
|
542
548
|
|
|
543
549
|
let results: RunViewGenericResult[] = [];
|
|
544
550
|
for (const [index, data] of rawData.entries()) {
|
|
545
|
-
const
|
|
551
|
+
const request = dataSource.request();
|
|
552
|
+
const entityIdResult = await request.query(
|
|
546
553
|
`SELECT TOP 1 ID from [${this.MJCoreSchema}].vwEntities WHERE Name='${input[index].EntityName}'`
|
|
547
554
|
);
|
|
555
|
+
const entityId = entityIdResult.recordset;
|
|
548
556
|
const returnData: any[] = this.processRawData(data.Results, entityId[0].ID);
|
|
549
557
|
|
|
550
558
|
results.push({
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { expressMiddleware } from '@apollo/server/express4';
|
|
|
6
6
|
import { mergeSchemas } from '@graphql-tools/schema';
|
|
7
7
|
import { Metadata } from '@memberjunction/core';
|
|
8
8
|
import { setupSQLServerClient, SQLServerProviderConfigData, UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
9
|
+
import { extendConnectionPoolWithQuery } from './util.js';
|
|
9
10
|
import { default as BodyParser } from 'body-parser';
|
|
10
11
|
import compression from 'compression'; // Add compression middleware
|
|
11
12
|
import cors from 'cors';
|
|
@@ -18,13 +19,13 @@ import { sep } from 'node:path';
|
|
|
18
19
|
import 'reflect-metadata';
|
|
19
20
|
import { ReplaySubject } from 'rxjs';
|
|
20
21
|
import { BuildSchemaOptions, buildSchemaSync, GraphQLTimestamp } from 'type-graphql';
|
|
21
|
-
import
|
|
22
|
+
import sql from 'mssql';
|
|
22
23
|
import { WebSocketServer } from 'ws';
|
|
23
24
|
import buildApolloServer from './apolloServer/index.js';
|
|
24
25
|
import { configInfo, dbDatabase, dbHost, dbPort, dbUsername, graphqlPort, graphqlRootPath, mj_core_schema, websiteRunFromPackage, RESTApiOptions } from './config.js';
|
|
25
26
|
import { contextFunction, getUserPayload } from './context.js';
|
|
26
27
|
import { requireSystemUserDirective, publicDirective } from './directives/index.js';
|
|
27
|
-
import
|
|
28
|
+
import createMSSQLConfig from './orm.js';
|
|
28
29
|
import { setupRESTEndpoints } from './rest/setupRESTEndpoints.js';
|
|
29
30
|
|
|
30
31
|
import { LoadActionEntityServer } from '@memberjunction/actions';
|
|
@@ -54,6 +55,7 @@ export * from './generic/ResolverBase.js';
|
|
|
54
55
|
export * from './generic/RunViewResolver.js';
|
|
55
56
|
export * from './resolvers/RunTemplateResolver.js';
|
|
56
57
|
export * from './resolvers/RunAIPromptResolver.js';
|
|
58
|
+
export * from './resolvers/RunAIAgentResolver.js';
|
|
57
59
|
export * from './generic/KeyValuePairInput.js';
|
|
58
60
|
export * from './generic/KeyInputOutputTypes.js';
|
|
59
61
|
export * from './generic/DeleteOptionsInput.js';
|
|
@@ -105,31 +107,31 @@ export const serve = async (resolverPaths: Array<string>, app = createApp(), opt
|
|
|
105
107
|
console.log({ combinedResolverPaths, paths, cwd: process.cwd() });
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
const
|
|
110
|
+
const pool = new sql.ConnectionPool(createMSSQLConfig());
|
|
109
111
|
const setupComplete$ = new ReplaySubject(1);
|
|
110
|
-
await
|
|
112
|
+
await pool.connect();
|
|
111
113
|
|
|
112
|
-
const config = new SQLServerProviderConfigData(
|
|
114
|
+
const config = new SQLServerProviderConfigData(pool, '', mj_core_schema, cacheRefreshInterval);
|
|
113
115
|
await setupSQLServerClient(config); // datasource is already initialized, so we can setup the client right away
|
|
114
116
|
const md = new Metadata();
|
|
115
117
|
console.log(`Data Source has been initialized. ${md?.Entities ? md.Entities.length : 0} entities loaded.`);
|
|
116
118
|
|
|
117
|
-
const dataSources = [new DataSourceInfo({dataSource, type: 'Read-Write', host: dbHost, port: dbPort, database: dbDatabase, userName: dbUsername})];
|
|
119
|
+
const dataSources = [new DataSourceInfo({dataSource: pool, type: 'Read-Write', host: dbHost, port: dbPort, database: dbDatabase, userName: dbUsername})];
|
|
118
120
|
|
|
119
121
|
// Establish a second read-only connection to the database if dbReadOnlyUsername and dbReadOnlyPassword exist
|
|
120
|
-
let
|
|
122
|
+
let readOnlyPool: sql.ConnectionPool | null = null;
|
|
121
123
|
if (configInfo.dbReadOnlyUsername && configInfo.dbReadOnlyPassword) {
|
|
122
124
|
const readOnlyConfig = {
|
|
123
|
-
...
|
|
124
|
-
|
|
125
|
+
...createMSSQLConfig(),
|
|
126
|
+
user: configInfo.dbReadOnlyUsername,
|
|
125
127
|
password: configInfo.dbReadOnlyPassword,
|
|
126
128
|
};
|
|
127
|
-
|
|
128
|
-
await
|
|
129
|
+
readOnlyPool = new sql.ConnectionPool(readOnlyConfig);
|
|
130
|
+
await readOnlyPool.connect();
|
|
129
131
|
|
|
130
|
-
// since we created a read-only
|
|
131
|
-
dataSources.push(new DataSourceInfo({dataSource:
|
|
132
|
-
console.log('Read-only
|
|
132
|
+
// since we created a read-only pool, add it to the list of data sources
|
|
133
|
+
dataSources.push(new DataSourceInfo({dataSource: readOnlyPool, type: 'Read-Only', host: dbHost, port: dbPort, database: dbDatabase, userName: configInfo.dbReadOnlyUsername}));
|
|
134
|
+
console.log('Read-only Connection Pool has been initialized.');
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
setupComplete$.next(true);
|
|
@@ -224,7 +226,7 @@ export const serve = async (resolverPaths: Array<string>, app = createApp(), opt
|
|
|
224
226
|
expressMiddleware(apolloServer, {
|
|
225
227
|
context: contextFunction({
|
|
226
228
|
setupComplete$,
|
|
227
|
-
dataSource, // default read-write data source
|
|
229
|
+
dataSource: extendConnectionPoolWithQuery(pool), // default read-write data source
|
|
228
230
|
dataSources // all data source
|
|
229
231
|
}),
|
|
230
232
|
})
|
|
@@ -288,5 +290,6 @@ export const serve = async (resolverPaths: Array<string>, app = createApp(), opt
|
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
await new Promise<void>((resolve) => httpServer.listen({ port: graphqlPort }, resolve));
|
|
293
|
+
console.log(`📦 Connected to database: ${dbHost}:${dbPort}/${dbDatabase}`);
|
|
291
294
|
console.log(`🚀 Server ready at http://localhost:${graphqlPort}/`);
|
|
292
295
|
};
|
package/src/orm.ts
CHANGED
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
import
|
|
1
|
+
import sql from 'mssql';
|
|
2
2
|
import { configInfo, dbDatabase, dbHost, dbPassword, dbPort, dbUsername, dbInstanceName, dbTrustServerCertificate } from './config.js';
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
entities,
|
|
8
|
-
logging: false,
|
|
9
|
-
host: dbHost,
|
|
4
|
+
const createMSSQLConfig = (): sql.config => {
|
|
5
|
+
const mssqlConfig: sql.config = {
|
|
6
|
+
server: dbHost,
|
|
10
7
|
port: dbPort,
|
|
11
|
-
|
|
8
|
+
user: dbUsername,
|
|
12
9
|
password: dbPassword,
|
|
13
10
|
database: dbDatabase,
|
|
14
|
-
synchronize: false,
|
|
15
11
|
requestTimeout: configInfo.databaseSettings.requestTimeout,
|
|
16
12
|
connectionTimeout: configInfo.databaseSettings.connectionTimeout,
|
|
17
|
-
options: {
|
|
13
|
+
options: {
|
|
14
|
+
encrypt: true, // Use encryption
|
|
15
|
+
enableArithAbort: true,
|
|
16
|
+
},
|
|
18
17
|
};
|
|
18
|
+
|
|
19
19
|
if (dbInstanceName !== null && dbInstanceName !== undefined && dbInstanceName.trim().length > 0) {
|
|
20
|
-
|
|
21
|
-
...
|
|
20
|
+
mssqlConfig.options = {
|
|
21
|
+
...mssqlConfig.options,
|
|
22
22
|
instanceName: dbInstanceName,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
+
|
|
25
26
|
if (dbTrustServerCertificate !== null && dbTrustServerCertificate !== undefined) {
|
|
26
|
-
|
|
27
|
-
...
|
|
27
|
+
mssqlConfig.options = {
|
|
28
|
+
...mssqlConfig.options,
|
|
28
29
|
trustServerCertificate: dbTrustServerCertificate === 'Y',
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
//console.log({
|
|
33
|
-
return
|
|
33
|
+
//console.log({ mssqlConfig: { ...mssqlConfig, password: '***' } });
|
|
34
|
+
return mssqlConfig;
|
|
34
35
|
};
|
|
35
36
|
|
|
36
|
-
export default
|
|
37
|
+
export default createMSSQLConfig;
|
|
@@ -53,8 +53,8 @@ import {
|
|
|
53
53
|
UserNotificationEntity,
|
|
54
54
|
AIAgentEntityExtended
|
|
55
55
|
} from '@memberjunction/core-entities';
|
|
56
|
-
import { DataSource } from 'typeorm';
|
|
57
56
|
import { apiKey, baseUrl, configInfo, graphqlPort, mj_core_schema } from '../config.js';
|
|
57
|
+
import mssql from 'mssql';
|
|
58
58
|
|
|
59
59
|
import { registerEnumType } from 'type-graphql';
|
|
60
60
|
import { MJGlobal, CopyScalarsAndArrays } from '@memberjunction/global';
|
|
@@ -882,7 +882,7 @@ cycle.`);
|
|
|
882
882
|
*/
|
|
883
883
|
protected async buildBaseSkipRequest(
|
|
884
884
|
contextUser: UserInfo,
|
|
885
|
-
dataSource:
|
|
885
|
+
dataSource: mssql.ConnectionPool,
|
|
886
886
|
includeEntities: boolean,
|
|
887
887
|
includeQueries: boolean,
|
|
888
888
|
includeNotes: boolean,
|
|
@@ -955,7 +955,7 @@ cycle.`);
|
|
|
955
955
|
includeQueries: boolean,
|
|
956
956
|
includeNotes: boolean,
|
|
957
957
|
includeRequests: boolean,
|
|
958
|
-
dataSource:
|
|
958
|
+
dataSource: mssql.ConnectionPool,
|
|
959
959
|
contextUser: UserInfo,
|
|
960
960
|
forceEntitiesRefresh: boolean = false,
|
|
961
961
|
includeCallBackKeyAndAccessToken: boolean = false
|
|
@@ -1005,7 +1005,7 @@ cycle.`);
|
|
|
1005
1005
|
*/
|
|
1006
1006
|
protected async BuildSkipLearningCycleNewConversations(
|
|
1007
1007
|
lastLearningCycleDate: Date,
|
|
1008
|
-
dataSource:
|
|
1008
|
+
dataSource: mssql.ConnectionPool,
|
|
1009
1009
|
contextUser: UserInfo
|
|
1010
1010
|
): Promise<SkipConversation[]> {
|
|
1011
1011
|
try {
|
|
@@ -1143,7 +1143,7 @@ cycle.`);
|
|
|
1143
1143
|
includeNotes: boolean,
|
|
1144
1144
|
includeRequests: boolean,
|
|
1145
1145
|
contextUser: UserInfo,
|
|
1146
|
-
dataSource:
|
|
1146
|
+
dataSource: mssql.ConnectionPool,
|
|
1147
1147
|
forceEntitiesRefresh: boolean = false,
|
|
1148
1148
|
includeCallBackKeyAndAccessToken: boolean = false
|
|
1149
1149
|
): Promise<SkipAPIRequest> {
|
|
@@ -1191,7 +1191,7 @@ cycle.`);
|
|
|
1191
1191
|
* @param conversationId ID of the conversation
|
|
1192
1192
|
* @returns Array of artifacts associated with the conversation
|
|
1193
1193
|
*/
|
|
1194
|
-
protected async buildSkipAPIArtifacts(contextUser: UserInfo, dataSource:
|
|
1194
|
+
protected async buildSkipAPIArtifacts(contextUser: UserInfo, dataSource: mssql.ConnectionPool, conversationId: string): Promise<SkipAPIArtifact[]> {
|
|
1195
1195
|
const md = new Metadata();
|
|
1196
1196
|
const ei = md.EntityByName('MJ: Conversation Artifacts');
|
|
1197
1197
|
const rv = new RunView();
|
|
@@ -1507,7 +1507,7 @@ cycle.`);
|
|
|
1507
1507
|
* @param dataSource Database connection
|
|
1508
1508
|
* @returns Array of entity rows based on packing configuration
|
|
1509
1509
|
*/
|
|
1510
|
-
protected async PackEntityRows(e: EntityInfo, dataSource:
|
|
1510
|
+
protected async PackEntityRows(e: EntityInfo, dataSource: mssql.ConnectionPool): Promise<any[]> {
|
|
1511
1511
|
try {
|
|
1512
1512
|
if (e.RowsToPackWithSchema === 'None')
|
|
1513
1513
|
return [];
|
|
@@ -1545,12 +1545,13 @@ cycle.`);
|
|
|
1545
1545
|
break;
|
|
1546
1546
|
}
|
|
1547
1547
|
}
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1548
|
+
const request = new mssql.Request(dataSource);
|
|
1549
|
+
const result = await request.query(sql);
|
|
1550
|
+
if (!result || !result.recordset) {
|
|
1550
1551
|
return [];
|
|
1551
1552
|
}
|
|
1552
1553
|
else {
|
|
1553
|
-
return result;
|
|
1554
|
+
return result.recordset;
|
|
1554
1555
|
}
|
|
1555
1556
|
}
|
|
1556
1557
|
catch (e) {
|
|
@@ -1567,7 +1568,7 @@ cycle.`);
|
|
|
1567
1568
|
* @param dataSource Database connection
|
|
1568
1569
|
* @returns Array of possible values for the field
|
|
1569
1570
|
*/
|
|
1570
|
-
protected async PackFieldPossibleValues(f: EntityFieldInfo, dataSource:
|
|
1571
|
+
protected async PackFieldPossibleValues(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldValueInfo[]> {
|
|
1571
1572
|
try {
|
|
1572
1573
|
if (f.ValuesToPackWithSchema === 'None') {
|
|
1573
1574
|
return []; // don't pack anything
|
|
@@ -1617,15 +1618,16 @@ cycle.`);
|
|
|
1617
1618
|
* @param dataSource Database connection
|
|
1618
1619
|
* @returns Array of distinct values for the field
|
|
1619
1620
|
*/
|
|
1620
|
-
protected async GetFieldDistinctValues(f: EntityFieldInfo, dataSource:
|
|
1621
|
+
protected async GetFieldDistinctValues(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldValueInfo[]> {
|
|
1621
1622
|
try {
|
|
1622
1623
|
const sql = `SELECT DISTINCT ${f.Name} FROM ${f.SchemaName}.${f.BaseView}`;
|
|
1623
|
-
const
|
|
1624
|
-
|
|
1624
|
+
const request = new mssql.Request(dataSource);
|
|
1625
|
+
const result = await request.query(sql);
|
|
1626
|
+
if (!result || !result.recordset) {
|
|
1625
1627
|
return [];
|
|
1626
1628
|
}
|
|
1627
1629
|
else {
|
|
1628
|
-
return result.map((r) => {
|
|
1630
|
+
return result.recordset.map((r) => {
|
|
1629
1631
|
return {
|
|
1630
1632
|
value: r[f.Name],
|
|
1631
1633
|
displayValue: r[f.Name]
|
|
@@ -1652,7 +1654,7 @@ cycle.`);
|
|
|
1652
1654
|
* @param dataSource Database connection
|
|
1653
1655
|
* @returns Updated array of entity information
|
|
1654
1656
|
*/
|
|
1655
|
-
private async refreshSkipEntities(dataSource:
|
|
1657
|
+
private async refreshSkipEntities(dataSource: mssql.ConnectionPool): Promise<SkipEntityInfo[]> {
|
|
1656
1658
|
try {
|
|
1657
1659
|
const md = new Metadata();
|
|
1658
1660
|
const skipSpecialIncludeEntities = (configInfo.askSkip?.entitiesToSend?.includeEntitiesFromExcludedSchemas ?? [])
|
|
@@ -1695,7 +1697,7 @@ cycle.`);
|
|
|
1695
1697
|
* @param refreshIntervalMinutes Minutes before cache expires
|
|
1696
1698
|
* @returns Array of entity information
|
|
1697
1699
|
*/
|
|
1698
|
-
public async BuildSkipEntities(dataSource:
|
|
1700
|
+
public async BuildSkipEntities(dataSource: mssql.ConnectionPool, forceRefresh: boolean = false, refreshIntervalMinutes: number = 15): Promise<SkipEntityInfo[]> {
|
|
1699
1701
|
try {
|
|
1700
1702
|
const now = Date.now();
|
|
1701
1703
|
const cacheExpired = (now - AskSkipResolver.__lastRefreshTime) > (refreshIntervalMinutes * 60 * 1000);
|
|
@@ -1723,7 +1725,7 @@ cycle.`);
|
|
|
1723
1725
|
* @param dataSource Database connection
|
|
1724
1726
|
* @returns Packaged entity information
|
|
1725
1727
|
*/
|
|
1726
|
-
protected async PackSingleSkipEntityInfo(e: EntityInfo, dataSource:
|
|
1728
|
+
protected async PackSingleSkipEntityInfo(e: EntityInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityInfo> {
|
|
1727
1729
|
try {
|
|
1728
1730
|
const ret: SkipEntityInfo = {
|
|
1729
1731
|
id: e.ID,
|
|
@@ -1794,7 +1796,7 @@ cycle.`);
|
|
|
1794
1796
|
* @param dataSource Database connection
|
|
1795
1797
|
* @returns Packaged field information
|
|
1796
1798
|
*/
|
|
1797
|
-
protected async PackSingleSkipEntityField(f: EntityFieldInfo, dataSource:
|
|
1799
|
+
protected async PackSingleSkipEntityField(f: EntityFieldInfo, dataSource: mssql.ConnectionPool): Promise<SkipEntityFieldInfo> {
|
|
1798
1800
|
try {
|
|
1799
1801
|
return {
|
|
1800
1802
|
//id: f.ID,
|
|
@@ -1848,7 +1850,7 @@ cycle.`);
|
|
|
1848
1850
|
* @returns Object containing loaded entities and contexts
|
|
1849
1851
|
*/
|
|
1850
1852
|
protected async HandleSkipChatInitialObjectLoading(
|
|
1851
|
-
dataSource:
|
|
1853
|
+
dataSource: mssql.ConnectionPool,
|
|
1852
1854
|
ConversationId: string,
|
|
1853
1855
|
UserQuestion: string,
|
|
1854
1856
|
user: UserInfo,
|
|
@@ -1985,7 +1987,7 @@ cycle.`);
|
|
|
1985
1987
|
* @returns Array of messages in Skip format
|
|
1986
1988
|
*/
|
|
1987
1989
|
protected async LoadConversationDetailsIntoSkipMessages(
|
|
1988
|
-
dataSource:
|
|
1990
|
+
dataSource: mssql.ConnectionPool,
|
|
1989
1991
|
ConversationId: string,
|
|
1990
1992
|
maxHistoricalMessages?: number,
|
|
1991
1993
|
roleFilter?: string
|
|
@@ -2010,14 +2012,15 @@ cycle.`);
|
|
|
2010
2012
|
ConversationID = '${ConversationId}'${roleFilterClause}
|
|
2011
2013
|
ORDER
|
|
2012
2014
|
BY __mj_CreatedAt DESC`;
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2015
|
+
const request = new mssql.Request(dataSource);
|
|
2016
|
+
const result = await request.query(sql);
|
|
2017
|
+
if (!result || !result.recordset)
|
|
2015
2018
|
throw new Error(`Error running SQL: ${sql}`);
|
|
2016
2019
|
else {
|
|
2017
2020
|
// first, let's sort the result array into a local variable called returnData and in that we will sort by __mj_CreatedAt in ASCENDING order so we have the right chronological order
|
|
2018
2021
|
// the reason we're doing a LOCAL sort here is because in the SQL query above, we're sorting in DESCENDING order so we can use the TOP clause to limit the number of records and get the
|
|
2019
2022
|
// N most recent records. We want to sort in ASCENDING order because we want to send the messages to the Skip API in the order they were created.
|
|
2020
|
-
const returnData = result.sort((a: any, b: any) => {
|
|
2023
|
+
const returnData = result.recordset.sort((a: any, b: any) => {
|
|
2021
2024
|
const aDate = new Date(a.__mj_CreatedAt);
|
|
2022
2025
|
const bDate = new Date(b.__mj_CreatedAt);
|
|
2023
2026
|
return aDate.getTime() - bDate.getTime();
|
|
@@ -2123,7 +2126,7 @@ cycle.`);
|
|
|
2123
2126
|
input: SkipAPIRequest,
|
|
2124
2127
|
UserQuestion: string,
|
|
2125
2128
|
user: UserInfo,
|
|
2126
|
-
dataSource:
|
|
2129
|
+
dataSource: mssql.ConnectionPool,
|
|
2127
2130
|
ConversationId: string,
|
|
2128
2131
|
userPayload: UserPayload,
|
|
2129
2132
|
pubSub: PubSubEngine,
|
|
@@ -2371,7 +2374,7 @@ cycle.`);
|
|
|
2371
2374
|
apiResponse: SkipAPIAnalysisCompleteResponse,
|
|
2372
2375
|
UserQuestion: string,
|
|
2373
2376
|
user: UserInfo,
|
|
2374
|
-
dataSource:
|
|
2377
|
+
dataSource: mssql.ConnectionPool,
|
|
2375
2378
|
ConversationId: string,
|
|
2376
2379
|
userPayload: UserPayload,
|
|
2377
2380
|
pubSub: PubSubEngine,
|
|
@@ -2435,7 +2438,7 @@ cycle.`);
|
|
|
2435
2438
|
apiResponse: SkipAPIClarifyingQuestionResponse,
|
|
2436
2439
|
UserQuestion: string,
|
|
2437
2440
|
user: UserInfo,
|
|
2438
|
-
dataSource:
|
|
2441
|
+
dataSource: mssql.ConnectionPool,
|
|
2439
2442
|
ConversationId: string,
|
|
2440
2443
|
userPayload: UserPayload,
|
|
2441
2444
|
pubSub: PubSubEngine,
|
|
@@ -2509,7 +2512,7 @@ cycle.`);
|
|
|
2509
2512
|
apiResponse: SkipAPIDataRequestResponse,
|
|
2510
2513
|
UserQuestion: string,
|
|
2511
2514
|
user: UserInfo,
|
|
2512
|
-
dataSource:
|
|
2515
|
+
dataSource: mssql.ConnectionPool,
|
|
2513
2516
|
ConversationId: string,
|
|
2514
2517
|
userPayload: UserPayload,
|
|
2515
2518
|
pubSub: PubSubEngine,
|
|
@@ -2681,7 +2684,7 @@ cycle.`);
|
|
|
2681
2684
|
convoEntity: ConversationEntity,
|
|
2682
2685
|
pubSub: PubSubEngine,
|
|
2683
2686
|
userPayload: UserPayload,
|
|
2684
|
-
dataSource:
|
|
2687
|
+
dataSource: mssql.ConnectionPool,
|
|
2685
2688
|
startTime: Date
|
|
2686
2689
|
): Promise<{ AIMessageConversationDetailID: string }> {
|
|
2687
2690
|
const sTitle = apiResponse.title || apiResponse.reportTitle;
|
|
@@ -2722,9 +2725,10 @@ cycle.`);
|
|
|
2722
2725
|
const ei = md.EntityByName("MJ: Conversation Artifact Versions");
|
|
2723
2726
|
const sSQL = `SELECT ISNULL(MAX(Version),0) AS MaxVersion FROM [${ei.SchemaName}].[${ei.BaseView}] WHERE ConversationArtifactID = '${artifactId}'`;
|
|
2724
2727
|
try {
|
|
2725
|
-
const
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
+
const request = new mssql.Request(dataSource);
|
|
2729
|
+
const result = await request.query(sSQL);
|
|
2730
|
+
if (result && result.recordset && result.recordset.length > 0) {
|
|
2731
|
+
newVersion = result.recordset[0].MaxVersion + 1;
|
|
2728
2732
|
}
|
|
2729
2733
|
else {
|
|
2730
2734
|
LogError(`Error getting max version for artifact ID: ${artifactId}`, undefined, result);
|
|
@@ -2,11 +2,12 @@ import { EntityPermissionType } from '@memberjunction/core';
|
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
3
|
import { Arg, Ctx, Query, Resolver, InputType, Field } from 'type-graphql';
|
|
4
4
|
import { Entity_, EntityResolverBase } from '../generated/generated.js';
|
|
5
|
+
import sql from 'mssql';
|
|
5
6
|
|
|
6
7
|
@Resolver(Entity_)
|
|
7
8
|
export class EntityResolver extends EntityResolverBase {
|
|
8
9
|
@Query(() => [Entity_])
|
|
9
|
-
EntitiesBySchemas(
|
|
10
|
+
async EntitiesBySchemas(
|
|
10
11
|
@Ctx() { dataSource, userPayload }: AppContext,
|
|
11
12
|
@Arg('IncludeSchemas', () => [String], { nullable: true }) IncludeSchemas?: string[],
|
|
12
13
|
@Arg('ExcludeSchemas', () => [String], { nullable: true }) ExcludeSchemas?: string[]
|
|
@@ -30,6 +31,8 @@ export class EntityResolver extends EntityResolverBase {
|
|
|
30
31
|
else totalWhere = ` WHERE ${rlsWhere}`;
|
|
31
32
|
}
|
|
32
33
|
const sSQL = `SELECT * FROM [${this.MJCoreSchema}].vwEntities${totalWhere}`;
|
|
33
|
-
|
|
34
|
+
const request = new sql.Request(dataSource);
|
|
35
|
+
const result = await request.query(sSQL);
|
|
36
|
+
return result.recordset;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
@@ -3,6 +3,7 @@ import { FileCategoryEntity, FileEntity } from '@memberjunction/core-entities';
|
|
|
3
3
|
import { AppContext, Arg, Ctx, DeleteOptionsInput, Int, Mutation } from '@memberjunction/server';
|
|
4
4
|
import { mj_core_schema } from '../config.js';
|
|
5
5
|
import { FileCategoryResolver as FileCategoryResolverBase, FileCategory_ } from '../generated/generated.js';
|
|
6
|
+
import sql from 'mssql';
|
|
6
7
|
|
|
7
8
|
export class FileResolver extends FileCategoryResolverBase {
|
|
8
9
|
@Mutation(() => FileCategory_)
|
|
@@ -29,7 +30,9 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
29
30
|
const returnValue = fileCategoryEntity.GetAll();
|
|
30
31
|
|
|
31
32
|
// Any files using the deleted category fall back to its parent
|
|
32
|
-
|
|
33
|
+
const transaction = new sql.Transaction(dataSource);
|
|
34
|
+
await transaction.begin();
|
|
35
|
+
try {
|
|
33
36
|
// SHOULD USE BaseEntity for each of these records to ensure object model
|
|
34
37
|
// is used everywhere - new code below. The below is SLOWER than a single
|
|
35
38
|
// Update statement, but it ensures that the object model is used everywhere
|
|
@@ -61,7 +64,11 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
await fileCategoryEntity.Delete();
|
|
64
|
-
|
|
67
|
+
await transaction.commit();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
await transaction.rollback();
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
65
72
|
|
|
66
73
|
await this.AfterDelete(dataSource, key); // fire event
|
|
67
74
|
return returnValue;
|
|
@@ -4,6 +4,7 @@ import { LogError, LogStatus, Metadata } from '@memberjunction/core';
|
|
|
4
4
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
5
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
6
|
import { GetReadOnlyDataSource } from '../util.js';
|
|
7
|
+
import sql from 'mssql';
|
|
7
8
|
|
|
8
9
|
@InputType()
|
|
9
10
|
export class GetDataInputType {
|
|
@@ -132,8 +133,9 @@ export class GetDataResolver {
|
|
|
132
133
|
const results = await Promise.allSettled(
|
|
133
134
|
input.Queries.map(async (query) => {
|
|
134
135
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
136
|
+
const request = new sql.Request(readOnlyDataSource);
|
|
137
|
+
const result = await request.query(query);
|
|
138
|
+
return { result: result.recordset, error: null };
|
|
137
139
|
} catch (err) {
|
|
138
140
|
return { result: null, error: err };
|
|
139
141
|
}
|