@memberjunction/server 2.95.0 → 2.97.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/context.d.ts.map +1 -1
- package/dist/context.js +4 -0
- package/dist/context.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +5 -4
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts +4 -4
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +9 -9
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/CreateQueryResolver.d.ts +2 -2
- package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/CreateQueryResolver.js +30 -18
- package/dist/resolvers/CreateQueryResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +2 -2
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +6 -5
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +4 -4
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +6 -5
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +10 -11
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +5 -6
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +2 -2
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.js +3 -3
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +8 -7
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js +4 -3
- package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +19 -14
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/ReportResolver.d.ts +2 -2
- package/dist/resolvers/ReportResolver.d.ts.map +1 -1
- package/dist/resolvers/ReportResolver.js +8 -6
- package/dist/resolvers/ReportResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +3 -7
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +8 -5
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +3 -7
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +10 -8
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts +2 -4
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +5 -4
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +7 -7
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.d.ts +3 -4
- package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.js +10 -68
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +3 -4
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/package.json +39 -39
- package/src/context.ts +5 -0
- package/src/resolvers/ActionResolver.ts +5 -4
- package/src/resolvers/AskSkipResolver.ts +13 -13
- package/src/resolvers/CreateQueryResolver.ts +34 -19
- package/src/resolvers/DatasetResolver.ts +5 -4
- package/src/resolvers/EntityRecordNameResolver.ts +8 -6
- package/src/resolvers/FileCategoryResolver.ts +9 -10
- package/src/resolvers/FileResolver.ts +6 -7
- package/src/resolvers/GetDataContextDataResolver.ts +2 -2
- package/src/resolvers/GetDataResolver.ts +2 -2
- package/src/resolvers/InfoResolver.ts +1 -1
- package/src/resolvers/MergeRecordsResolver.ts +7 -6
- package/src/resolvers/PotentialDuplicateRecordResolver.ts +3 -2
- package/src/resolvers/QueryResolver.ts +22 -15
- package/src/resolvers/ReportResolver.ts +9 -6
- package/src/resolvers/RunAIAgentResolver.ts +12 -4
- package/src/resolvers/RunAIPromptResolver.ts +12 -8
- package/src/resolvers/RunTemplateResolver.ts +5 -5
- package/src/resolvers/SqlLoggingConfigResolver.ts +7 -6
- package/src/resolvers/UserFavoriteResolver.ts +8 -67
- package/src/resolvers/UserViewResolver.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.97.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",
|
|
@@ -22,44 +22,44 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@apollo/server": "^4.9.1",
|
|
24
24
|
"@graphql-tools/utils": "^10.0.1",
|
|
25
|
-
"@memberjunction/actions": "2.
|
|
26
|
-
"@memberjunction/ai": "2.
|
|
27
|
-
"@memberjunction/ai-core-plus": "2.
|
|
28
|
-
"@memberjunction/ai-agents": "2.
|
|
29
|
-
"@memberjunction/aiengine": "2.
|
|
30
|
-
"@memberjunction/ai-prompts": "2.
|
|
31
|
-
"@memberjunction/ai-agent-manager-actions": "2.
|
|
32
|
-
"@memberjunction/ai-mistral": "2.
|
|
33
|
-
"@memberjunction/ai-openai": "2.
|
|
34
|
-
"@memberjunction/ai-anthropic": "2.
|
|
35
|
-
"@memberjunction/ai-groq": "2.
|
|
36
|
-
"@memberjunction/ai-cerebras": "2.
|
|
37
|
-
"@memberjunction/ai-lmstudio": "2.
|
|
38
|
-
"@memberjunction/ai-openrouter": "2.
|
|
39
|
-
"@memberjunction/ai-ollama": "2.
|
|
40
|
-
"@memberjunction/ai-vectors-pinecone": "2.
|
|
41
|
-
"@memberjunction/ai-local-embeddings": "2.
|
|
42
|
-
"@memberjunction/core": "2.
|
|
43
|
-
"@memberjunction/core-actions": "2.
|
|
44
|
-
"@memberjunction/actions-apollo": "2.
|
|
45
|
-
"@memberjunction/actions-bizapps-accounting": "2.
|
|
46
|
-
"@memberjunction/actions-bizapps-crm": "2.
|
|
47
|
-
"@memberjunction/actions-bizapps-lms": "2.
|
|
48
|
-
"@memberjunction/actions-bizapps-social": "2.
|
|
49
|
-
"@memberjunction/core-entities": "2.
|
|
50
|
-
"@memberjunction/core-entities-server": "2.
|
|
51
|
-
"@memberjunction/data-context": "2.
|
|
52
|
-
"@memberjunction/data-context-server": "2.
|
|
53
|
-
"@memberjunction/doc-utils": "2.
|
|
54
|
-
"@memberjunction/entity-communications-server": "2.
|
|
55
|
-
"@memberjunction/external-change-detection": "2.
|
|
56
|
-
"@memberjunction/global": "2.
|
|
57
|
-
"@memberjunction/graphql-dataprovider": "2.
|
|
58
|
-
"@memberjunction/queue": "2.
|
|
59
|
-
"@memberjunction/skip-types": "2.
|
|
60
|
-
"@memberjunction/sqlserver-dataprovider": "2.
|
|
61
|
-
"@memberjunction/storage": "2.
|
|
62
|
-
"@memberjunction/templates": "2.
|
|
25
|
+
"@memberjunction/actions": "2.97.0",
|
|
26
|
+
"@memberjunction/ai": "2.97.0",
|
|
27
|
+
"@memberjunction/ai-core-plus": "2.97.0",
|
|
28
|
+
"@memberjunction/ai-agents": "2.97.0",
|
|
29
|
+
"@memberjunction/aiengine": "2.97.0",
|
|
30
|
+
"@memberjunction/ai-prompts": "2.97.0",
|
|
31
|
+
"@memberjunction/ai-agent-manager-actions": "2.97.0",
|
|
32
|
+
"@memberjunction/ai-mistral": "2.97.0",
|
|
33
|
+
"@memberjunction/ai-openai": "2.97.0",
|
|
34
|
+
"@memberjunction/ai-anthropic": "2.97.0",
|
|
35
|
+
"@memberjunction/ai-groq": "2.97.0",
|
|
36
|
+
"@memberjunction/ai-cerebras": "2.97.0",
|
|
37
|
+
"@memberjunction/ai-lmstudio": "2.97.0",
|
|
38
|
+
"@memberjunction/ai-openrouter": "2.97.0",
|
|
39
|
+
"@memberjunction/ai-ollama": "2.97.0",
|
|
40
|
+
"@memberjunction/ai-vectors-pinecone": "2.97.0",
|
|
41
|
+
"@memberjunction/ai-local-embeddings": "2.97.0",
|
|
42
|
+
"@memberjunction/core": "2.97.0",
|
|
43
|
+
"@memberjunction/core-actions": "2.97.0",
|
|
44
|
+
"@memberjunction/actions-apollo": "2.97.0",
|
|
45
|
+
"@memberjunction/actions-bizapps-accounting": "2.97.0",
|
|
46
|
+
"@memberjunction/actions-bizapps-crm": "2.97.0",
|
|
47
|
+
"@memberjunction/actions-bizapps-lms": "2.97.0",
|
|
48
|
+
"@memberjunction/actions-bizapps-social": "2.97.0",
|
|
49
|
+
"@memberjunction/core-entities": "2.97.0",
|
|
50
|
+
"@memberjunction/core-entities-server": "2.97.0",
|
|
51
|
+
"@memberjunction/data-context": "2.97.0",
|
|
52
|
+
"@memberjunction/data-context-server": "2.97.0",
|
|
53
|
+
"@memberjunction/doc-utils": "2.97.0",
|
|
54
|
+
"@memberjunction/entity-communications-server": "2.97.0",
|
|
55
|
+
"@memberjunction/external-change-detection": "2.97.0",
|
|
56
|
+
"@memberjunction/global": "2.97.0",
|
|
57
|
+
"@memberjunction/graphql-dataprovider": "2.97.0",
|
|
58
|
+
"@memberjunction/queue": "2.97.0",
|
|
59
|
+
"@memberjunction/skip-types": "2.97.0",
|
|
60
|
+
"@memberjunction/sqlserver-dataprovider": "2.97.0",
|
|
61
|
+
"@memberjunction/storage": "2.97.0",
|
|
62
|
+
"@memberjunction/templates": "2.97.0",
|
|
63
63
|
"@types/compression": "^1.7.5",
|
|
64
64
|
"@types/cors": "^2.8.13",
|
|
65
65
|
"@types/jsonwebtoken": "9.0.6",
|
package/src/context.ts
CHANGED
|
@@ -15,6 +15,7 @@ import e from 'express';
|
|
|
15
15
|
import { DatabaseProviderBase } from '@memberjunction/core';
|
|
16
16
|
import { SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
17
17
|
import { AuthProviderFactory } from './auth/AuthProviderFactory.js';
|
|
18
|
+
import { Metadata } from '@memberjunction/core';
|
|
18
19
|
|
|
19
20
|
const verifyAsync = async (issuer: string, token: string): Promise<jwt.JwtPayload> =>
|
|
20
21
|
new Promise((resolve, reject) => {
|
|
@@ -157,6 +158,10 @@ export const contextFunction =
|
|
|
157
158
|
apiKey
|
|
158
159
|
);
|
|
159
160
|
|
|
161
|
+
if (Metadata.Provider.Entities.length === 0 ) {
|
|
162
|
+
console.warn('WARNING: No entities found in global/shared metadata, this can often be due to the use of **global** Metadata/RunView/DB Providers in a multi-user environment. Check your code to make sure you are using the providers passed to you in AppContext by MJServer and not calling new Metadata() new RunView() new RunQuery() and similar patterns as those are unstable at times in multi-user server environments!!!');
|
|
163
|
+
}
|
|
164
|
+
|
|
160
165
|
// now create a new instance of SQLServerDataProvider for each request
|
|
161
166
|
const config = new SQLServerProviderConfigData(dataSource, mj_core_schema, 0, undefined, undefined, false);
|
|
162
167
|
const p = new SQLServerDataProvider();
|
|
@@ -5,8 +5,9 @@ import { Metadata, UserInfo, BaseEntity, CompositeKey, KeyValuePair, LogError }
|
|
|
5
5
|
import { ActionParam, ActionResult } from "@memberjunction/actions-base";
|
|
6
6
|
import { Field, InputType, ObjectType } from "type-graphql";
|
|
7
7
|
import { KeyValuePairInput } from "../generic/KeyValuePairInput.js";
|
|
8
|
-
import { AppContext } from "../types.js";
|
|
8
|
+
import { AppContext, ProviderInfo } from "../types.js";
|
|
9
9
|
import { CopyScalarsAndArrays } from "@memberjunction/global";
|
|
10
|
+
import { GetReadOnlyProvider } from "../util.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Input type for action parameters
|
|
@@ -341,7 +342,7 @@ export class ActionResolver {
|
|
|
341
342
|
|
|
342
343
|
// Add entity object if we have entity information and primary key
|
|
343
344
|
if ((input.EntityID || input.EntityName) && input.PrimaryKey && input.PrimaryKey.KeyValuePairs.length > 0) {
|
|
344
|
-
await this.addEntityObject(params, input, user);
|
|
345
|
+
await this.addEntityObject(ctx.providers, params, input, user);
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
// Add other parameters
|
|
@@ -400,8 +401,8 @@ export class ActionResolver {
|
|
|
400
401
|
* @param user The authenticated user
|
|
401
402
|
* @private
|
|
402
403
|
*/
|
|
403
|
-
private async addEntityObject(params: any, input: EntityActionInput, user: UserInfo): Promise<void> {
|
|
404
|
-
const md =
|
|
404
|
+
private async addEntityObject(providers: Array<ProviderInfo>, params: any, input: EntityActionInput, user: UserInfo): Promise<void> {
|
|
405
|
+
const md = GetReadOnlyProvider(providers);
|
|
405
406
|
|
|
406
407
|
// Find the entity by ID or name
|
|
407
408
|
let entity;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, Mutation, ObjectType, PubSub, PubSubEngine, Query, Resolver } from 'type-graphql';
|
|
2
|
-
import { LogError, LogStatus, Metadata, RunView, UserInfo, CompositeKey, EntityFieldInfo, EntityInfo, EntityRelationshipInfo, EntitySaveOptions, EntityDeleteOptions } from '@memberjunction/core';
|
|
2
|
+
import { LogError, LogStatus, Metadata, RunView, UserInfo, CompositeKey, EntityFieldInfo, EntityInfo, EntityRelationshipInfo, EntitySaveOptions, EntityDeleteOptions, IMetadataProvider } from '@memberjunction/core';
|
|
3
3
|
import { AppContext, UserPayload, MJ_SERVER_EVENT_CODE } from '../types.js';
|
|
4
4
|
import { BehaviorSubject } from 'rxjs';
|
|
5
5
|
import { take } from 'rxjs/operators';
|
|
@@ -59,7 +59,7 @@ import mssql from 'mssql';
|
|
|
59
59
|
|
|
60
60
|
import { registerEnumType } from 'type-graphql';
|
|
61
61
|
import { MJGlobal, CopyScalarsAndArrays } from '@memberjunction/global';
|
|
62
|
-
import { sendPostRequest } from '../util.js';
|
|
62
|
+
import { GetReadWriteProvider, sendPostRequest } from '../util.js';
|
|
63
63
|
import { GetAIAPIKey } from '@memberjunction/ai';
|
|
64
64
|
import { CompositeKeyInputType } from '../generic/KeyInputOutputTypes.js';
|
|
65
65
|
import { AIEngine } from '@memberjunction/aiengine';
|
|
@@ -479,7 +479,7 @@ export class AskSkipResolver {
|
|
|
479
479
|
@Arg('ConversationId', () => String) ConversationId: string,
|
|
480
480
|
@Arg('EntityName', () => String) EntityName: string,
|
|
481
481
|
@Arg('CompositeKey', () => CompositeKeyInputType) compositeKey: CompositeKeyInputType,
|
|
482
|
-
@Ctx() { dataSource, userPayload }: AppContext,
|
|
482
|
+
@Ctx() { dataSource, userPayload, providers }: AppContext,
|
|
483
483
|
@PubSub() pubSub: PubSubEngine
|
|
484
484
|
) {
|
|
485
485
|
// In this function we're simply going to call the Skip API and pass along the message from the user
|
|
@@ -498,14 +498,14 @@ export class AskSkipResolver {
|
|
|
498
498
|
);
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
-
const md =
|
|
501
|
+
const md = GetReadWriteProvider(providers);
|
|
502
502
|
const { convoEntity, dataContextEntity, convoDetailEntity, dataContext } = await this.HandleSkipChatInitialObjectLoading(
|
|
503
503
|
dataSource,
|
|
504
504
|
ConversationId,
|
|
505
505
|
UserQuestion,
|
|
506
506
|
user,
|
|
507
507
|
userPayload,
|
|
508
|
-
md,
|
|
508
|
+
md as unknown as Metadata,
|
|
509
509
|
null
|
|
510
510
|
);
|
|
511
511
|
|
|
@@ -558,7 +558,7 @@ export class AskSkipResolver {
|
|
|
558
558
|
*/
|
|
559
559
|
@Mutation(() => AskSkipResultType)
|
|
560
560
|
async ExecuteAskSkipLearningCycle(
|
|
561
|
-
@Ctx() { dataSource, userPayload }: AppContext,
|
|
561
|
+
@Ctx() { dataSource, userPayload, providers }: AppContext,
|
|
562
562
|
@Arg('ForceEntityRefresh', () => Boolean, { nullable: true }) ForceEntityRefresh?: boolean
|
|
563
563
|
) {
|
|
564
564
|
const skipConfigInfo = configInfo.askSkip;
|
|
@@ -612,7 +612,7 @@ export class AskSkipResolver {
|
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
// Get the Skip agent ID
|
|
615
|
-
const md =
|
|
615
|
+
const md = GetReadWriteProvider(providers);
|
|
616
616
|
const skipAgent = AIEngine.Instance.GetAgentByName('Skip');
|
|
617
617
|
if (!skipAgent) {
|
|
618
618
|
throw new Error("Skip agent not found in AIEngine");
|
|
@@ -1467,11 +1467,11 @@ cycle.`);
|
|
|
1467
1467
|
@Query(() => ReattachConversationResponse)
|
|
1468
1468
|
async ReattachToProcessingConversation(
|
|
1469
1469
|
@Arg('ConversationId', () => String) ConversationId: string,
|
|
1470
|
-
@Ctx() { userPayload }: AppContext,
|
|
1470
|
+
@Ctx() { userPayload, providers }: AppContext,
|
|
1471
1471
|
@PubSub() pubSub: PubSubEngine
|
|
1472
1472
|
): Promise<ReattachConversationResponse | null> {
|
|
1473
1473
|
try {
|
|
1474
|
-
const md =
|
|
1474
|
+
const md = GetReadWriteProvider(providers);
|
|
1475
1475
|
const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
|
|
1476
1476
|
if (!user) {
|
|
1477
1477
|
LogError(`User ${userPayload.email} not found in UserCache`);
|
|
@@ -1581,13 +1581,13 @@ cycle.`);
|
|
|
1581
1581
|
async ExecuteAskSkipAnalysisQuery(
|
|
1582
1582
|
@Arg('UserQuestion', () => String) UserQuestion: string,
|
|
1583
1583
|
@Arg('ConversationId', () => String) ConversationId: string,
|
|
1584
|
-
@Ctx() { dataSource, userPayload }: AppContext,
|
|
1584
|
+
@Ctx() { dataSource, userPayload, providers }: AppContext,
|
|
1585
1585
|
@PubSub() pubSub: PubSubEngine,
|
|
1586
1586
|
@Arg('DataContextId', () => String, { nullable: true }) DataContextId?: string,
|
|
1587
1587
|
@Arg('ForceEntityRefresh', () => Boolean, { nullable: true }) ForceEntityRefresh?: boolean,
|
|
1588
1588
|
@Arg('StartTime', () => Date, { nullable: true }) StartTime?: Date
|
|
1589
1589
|
) {
|
|
1590
|
-
const md =
|
|
1590
|
+
const md = GetReadWriteProvider(providers);
|
|
1591
1591
|
const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email.trim().toLowerCase());
|
|
1592
1592
|
if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
|
|
1593
1593
|
|
|
@@ -1600,7 +1600,7 @@ cycle.`);
|
|
|
1600
1600
|
UserQuestion,
|
|
1601
1601
|
user,
|
|
1602
1602
|
userPayload,
|
|
1603
|
-
md,
|
|
1603
|
+
md as unknown as Metadata,
|
|
1604
1604
|
DataContextId
|
|
1605
1605
|
);
|
|
1606
1606
|
|
|
@@ -1625,7 +1625,7 @@ cycle.`);
|
|
|
1625
1625
|
ConversationId,
|
|
1626
1626
|
userPayload,
|
|
1627
1627
|
pubSub,
|
|
1628
|
-
md,
|
|
1628
|
+
md as unknown as Metadata,
|
|
1629
1629
|
convoEntity,
|
|
1630
1630
|
convoDetailEntity,
|
|
1631
1631
|
dataContext,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType, Resolver, PubSub, PubSubEngine } from 'type-graphql';
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
|
-
import { LogError, Metadata, RunView, UserInfo, CompositeKey } from '@memberjunction/core';
|
|
3
|
+
import { LogError, Metadata, RunView, UserInfo, CompositeKey, DatabaseProviderBase } from '@memberjunction/core';
|
|
4
4
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
5
5
|
import { QueryCategoryEntity, QueryPermissionEntity } from '@memberjunction/core-entities';
|
|
6
6
|
import { QueryResolver } from '../generated/generated.js';
|
|
7
|
-
import { GetReadWriteProvider } from '../util.js';
|
|
7
|
+
import { GetReadOnlyProvider, GetReadWriteProvider } from '../util.js';
|
|
8
8
|
import { DeleteOptionsInput } from '../generic/DeleteOptionsInput.js';
|
|
9
9
|
import { QueryEntityExtended } from '@memberjunction/core-entities-server';
|
|
10
10
|
|
|
@@ -311,13 +311,12 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
311
311
|
try {
|
|
312
312
|
// Handle CategoryPath if provided
|
|
313
313
|
let finalCategoryID = input.CategoryID;
|
|
314
|
+
const provider = GetReadWriteProvider(context.providers);
|
|
314
315
|
if (input.CategoryPath) {
|
|
315
|
-
|
|
316
|
-
finalCategoryID = await this.findOrCreateCategoryPath(input.CategoryPath, md, context.userPayload.userRecord);
|
|
316
|
+
finalCategoryID = await this.findOrCreateCategoryPath(input.CategoryPath, provider, context.userPayload.userRecord);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
// Use QueryEntityExtended which handles AI processing
|
|
320
|
-
const provider = GetReadWriteProvider(context.providers);
|
|
321
320
|
const record = await provider.GetEntityObject<QueryEntityExtended>("Queries", context.userPayload.userRecord);
|
|
322
321
|
|
|
323
322
|
// Set the fields from input, handling CategoryPath resolution
|
|
@@ -345,7 +344,7 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
345
344
|
const queryID = record.ID;
|
|
346
345
|
|
|
347
346
|
if (input.Permissions && input.Permissions.length > 0) {
|
|
348
|
-
await this.createPermissions(input.Permissions, queryID, context.userPayload.userRecord);
|
|
347
|
+
await this.createPermissions(provider, input.Permissions, queryID, context.userPayload.userRecord);
|
|
349
348
|
await record.RefreshRelatedMetadata(true); // force DB update since we just created new permissions
|
|
350
349
|
}
|
|
351
350
|
|
|
@@ -354,7 +353,12 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
354
353
|
QueryData: JSON.stringify(record.GetAll()),
|
|
355
354
|
Fields: record.QueryFields,
|
|
356
355
|
Parameters: record.QueryParameters,
|
|
357
|
-
Entities: record.QueryEntities
|
|
356
|
+
Entities: record.QueryEntities.map(e => {
|
|
357
|
+
return {
|
|
358
|
+
...e,
|
|
359
|
+
EntityName: e.Entity // alias this to fix variable name mismatch
|
|
360
|
+
}
|
|
361
|
+
}),
|
|
358
362
|
Permissions: record.QueryPermissions
|
|
359
363
|
};
|
|
360
364
|
}
|
|
@@ -374,13 +378,12 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
374
378
|
}
|
|
375
379
|
}
|
|
376
380
|
|
|
377
|
-
protected async createPermissions(permissions: QueryPermissionInputType[], queryID: string, contextUser: UserInfo): Promise<QueryPermissionType[]> {
|
|
381
|
+
protected async createPermissions(p: DatabaseProviderBase, permissions: QueryPermissionInputType[], queryID: string, contextUser: UserInfo): Promise<QueryPermissionType[]> {
|
|
378
382
|
// Create permissions if provided
|
|
379
383
|
const createdPermissions: QueryPermissionType[] = [];
|
|
380
384
|
if (permissions && permissions.length > 0) {
|
|
381
|
-
const md = new Metadata();
|
|
382
385
|
for (const perm of permissions) {
|
|
383
|
-
const permissionEntity = await
|
|
386
|
+
const permissionEntity = await p.GetEntityObject<QueryPermissionEntity>('Query Permissions', contextUser);
|
|
384
387
|
if (permissionEntity) {
|
|
385
388
|
permissionEntity.QueryID = queryID;
|
|
386
389
|
permissionEntity.RoleID = perm.RoleID;
|
|
@@ -427,8 +430,20 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
427
430
|
// Handle CategoryPath if provided
|
|
428
431
|
let finalCategoryID = input.CategoryID;
|
|
429
432
|
if (input.CategoryPath) {
|
|
430
|
-
|
|
431
|
-
|
|
433
|
+
finalCategoryID = await this.findOrCreateCategoryPath(input.CategoryPath, provider, context.userPayload.userRecord);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// now make sure there is NO existing query by the same name in the specified category
|
|
437
|
+
const existingQueryResult = await provider.RunView({
|
|
438
|
+
EntityName: 'Queries',
|
|
439
|
+
ExtraFilter: `Name='${input.Name}' AND CategoryID='${finalCategoryID}'`
|
|
440
|
+
}, context.userPayload.userRecord);
|
|
441
|
+
if (existingQueryResult.Success && existingQueryResult.Results?.length > 0) {
|
|
442
|
+
// we have a match! Let's return an error
|
|
443
|
+
return {
|
|
444
|
+
Success: false,
|
|
445
|
+
ErrorMessage: `Query with name '${input.Name}' already exists in the specified ${input.CategoryID ? 'category' : 'categoryPath'}`
|
|
446
|
+
};
|
|
432
447
|
}
|
|
433
448
|
|
|
434
449
|
// Update fields that were provided
|
|
@@ -481,7 +496,7 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
481
496
|
}
|
|
482
497
|
|
|
483
498
|
// Create new permissions
|
|
484
|
-
await this.createPermissions(input.Permissions, queryID, context.userPayload.userRecord);
|
|
499
|
+
await this.createPermissions(provider, input.Permissions, queryID, context.userPayload.userRecord);
|
|
485
500
|
|
|
486
501
|
// Refresh the metadata to get updated permissions
|
|
487
502
|
await queryEntity.RefreshRelatedMetadata(true);
|
|
@@ -608,7 +623,7 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
608
623
|
* @param contextUser - User context for operations
|
|
609
624
|
* @returns The ID of the final category in the path
|
|
610
625
|
*/
|
|
611
|
-
private async findOrCreateCategoryPath(categoryPath: string,
|
|
626
|
+
private async findOrCreateCategoryPath(categoryPath: string, p: DatabaseProviderBase, contextUser: UserInfo): Promise<string> {
|
|
612
627
|
if (!categoryPath || categoryPath.trim() === '') {
|
|
613
628
|
throw new Error('CategoryPath cannot be empty');
|
|
614
629
|
}
|
|
@@ -625,7 +640,7 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
625
640
|
const categoryName = pathParts[i];
|
|
626
641
|
|
|
627
642
|
// Look for existing category at this level
|
|
628
|
-
const existingCategory = await this.findCategoryByNameAndParent(categoryName, currentParentID, contextUser);
|
|
643
|
+
const existingCategory = await this.findCategoryByNameAndParent(p, categoryName, currentParentID, contextUser);
|
|
629
644
|
|
|
630
645
|
if (existingCategory) {
|
|
631
646
|
currentCategoryID = existingCategory.ID;
|
|
@@ -633,7 +648,7 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
633
648
|
} else {
|
|
634
649
|
try {
|
|
635
650
|
// Create new category
|
|
636
|
-
const newCategory = await
|
|
651
|
+
const newCategory = await p.GetEntityObject<QueryCategoryEntity>("Query Categories", contextUser);
|
|
637
652
|
if (!newCategory) {
|
|
638
653
|
throw new Error(`Failed to create entity object for Query Categories`);
|
|
639
654
|
}
|
|
@@ -652,7 +667,7 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
652
667
|
currentParentID = newCategory.ID;
|
|
653
668
|
|
|
654
669
|
// Refresh metadata after each category creation to ensure it's available for subsequent lookups
|
|
655
|
-
await
|
|
670
|
+
await p.Refresh();
|
|
656
671
|
} catch (error) {
|
|
657
672
|
throw new Error(`Failed to create category '${categoryName}': ${error instanceof Error ? error.message : String(error)}`);
|
|
658
673
|
}
|
|
@@ -673,9 +688,9 @@ export class QueryResolverExtended extends QueryResolver {
|
|
|
673
688
|
* @param contextUser - User context for database operations
|
|
674
689
|
* @returns The matching category entity or null if not found
|
|
675
690
|
*/
|
|
676
|
-
private async findCategoryByNameAndParent(categoryName: string, parentID: string | null, contextUser: UserInfo): Promise<QueryCategoryEntity | null> {
|
|
691
|
+
private async findCategoryByNameAndParent(provider: DatabaseProviderBase, categoryName: string, parentID: string | null, contextUser: UserInfo): Promise<QueryCategoryEntity | null> {
|
|
677
692
|
try {
|
|
678
|
-
const rv =
|
|
693
|
+
const rv = provider;
|
|
679
694
|
const parentFilter = parentID ? `ParentID='${parentID}'` : 'ParentID IS NULL';
|
|
680
695
|
const nameFilter = `LOWER(Name) = LOWER('${categoryName.replace(/'/g, "''")}')`; // Escape single quotes
|
|
681
696
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, Int, ObjectType, Query, Resolver } from 'type-graphql';
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
3
|
import { LogError, Metadata } from '@memberjunction/core';
|
|
4
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
4
5
|
|
|
5
6
|
@ObjectType()
|
|
6
7
|
export class DatasetResultType {
|
|
@@ -38,11 +39,11 @@ export class DatasetResolverExtended {
|
|
|
38
39
|
@Query(() => DatasetResultType)
|
|
39
40
|
async GetDatasetByName(
|
|
40
41
|
@Arg('DatasetName', () => String) DatasetName: string,
|
|
41
|
-
@Ctx() {}: AppContext,
|
|
42
|
+
@Ctx() {providers}: AppContext,
|
|
42
43
|
@Arg('ItemFilters', () => [DatasetItemFilterTypeGQL], { nullable: 'itemsAndList' }) ItemFilters?: DatasetItemFilterTypeGQL[]
|
|
43
44
|
) {
|
|
44
45
|
try {
|
|
45
|
-
const md =
|
|
46
|
+
const md = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
46
47
|
const result = await md.GetDatasetByName(DatasetName, ItemFilters);
|
|
47
48
|
if (result) {
|
|
48
49
|
return {
|
|
@@ -89,11 +90,11 @@ export class DatasetStatusResolver {
|
|
|
89
90
|
@Query(() => DatasetStatusResultType)
|
|
90
91
|
async GetDatasetStatusByName(
|
|
91
92
|
@Arg('DatasetName', () => String) DatasetName: string,
|
|
92
|
-
@Ctx() {}: AppContext,
|
|
93
|
+
@Ctx() {providers}: AppContext,
|
|
93
94
|
@Arg('ItemFilters', () => [DatasetItemFilterTypeGQL], { nullable: 'itemsAndList' }) ItemFilters?: DatasetItemFilterTypeGQL[]
|
|
94
95
|
) {
|
|
95
96
|
try {
|
|
96
|
-
const md =
|
|
97
|
+
const md = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
97
98
|
const result = await md.GetDatasetStatusByName(DatasetName, ItemFilters);
|
|
98
99
|
if (result) {
|
|
99
100
|
return {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Metadata, CompositeKey } from '@memberjunction/core';
|
|
1
|
+
import { Metadata, CompositeKey, DatabaseProviderBase } from '@memberjunction/core';
|
|
2
2
|
import { Arg, Ctx, Field, InputType, ObjectType, Query, Resolver } from 'type-graphql';
|
|
3
3
|
import { AppContext } from '../types.js';
|
|
4
4
|
import { CompositeKeyInputType, CompositeKeyOutputType } from '../generic/KeyInputOutputTypes.js';
|
|
5
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
5
6
|
|
|
6
7
|
@InputType()
|
|
7
8
|
export class EntityRecordNameInput {
|
|
@@ -36,26 +37,27 @@ export class EntityRecordNameResolver {
|
|
|
36
37
|
async GetEntityRecordName(
|
|
37
38
|
@Arg('EntityName', () => String) EntityName: string,
|
|
38
39
|
@Arg('CompositeKey', () => CompositeKeyInputType) primaryKey: CompositeKey,
|
|
39
|
-
@Ctx() { userPayload }: AppContext
|
|
40
|
+
@Ctx() { providers, userPayload }: AppContext
|
|
40
41
|
): Promise<EntityRecordNameResult> {
|
|
41
|
-
const md =
|
|
42
|
+
const md = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
43
|
+
|
|
42
44
|
return await this.InnerGetEntityRecordName(md, EntityName, primaryKey);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
@Query(() => [EntityRecordNameResult])
|
|
46
48
|
async GetEntityRecordNames(
|
|
47
49
|
@Arg('info', () => [EntityRecordNameInput]) info: EntityRecordNameInput[],
|
|
48
|
-
@Ctx() {}: AppContext
|
|
50
|
+
@Ctx() {providers}: AppContext
|
|
49
51
|
): Promise<EntityRecordNameResult[]> {
|
|
50
52
|
const result: EntityRecordNameResult[] = [];
|
|
51
|
-
const md =
|
|
53
|
+
const md = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
52
54
|
for (const i of info) {
|
|
53
55
|
result.push(await this.InnerGetEntityRecordName(md, i.EntityName, i.CompositeKey));
|
|
54
56
|
}
|
|
55
57
|
return result;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
async InnerGetEntityRecordName(md:
|
|
60
|
+
async InnerGetEntityRecordName(md: DatabaseProviderBase, EntityName: string, primaryKey: CompositeKeyInputType): Promise<EntityRecordNameResult> {
|
|
59
61
|
const pk = new CompositeKey(primaryKey.KeyValuePairs);
|
|
60
62
|
const e = md.Entities.find((e) => e.Name === EntityName);
|
|
61
63
|
if (e) {
|
|
@@ -16,16 +16,15 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
16
16
|
) {
|
|
17
17
|
const key = new CompositeKey();
|
|
18
18
|
key.LoadFromSingleKeyValuePair('ID', ID);
|
|
19
|
-
const
|
|
19
|
+
const p = GetReadWriteProvider(providers);
|
|
20
20
|
|
|
21
|
-
if (!(await this.BeforeDelete(
|
|
21
|
+
if (!(await this.BeforeDelete(p, key))) {
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const md = new Metadata();
|
|
26
25
|
const user = this.GetUserFromPayload(userPayload);
|
|
27
|
-
const fileEntity = await
|
|
28
|
-
const fileCategoryEntity = await
|
|
26
|
+
const fileEntity = await p.GetEntityObject<FileEntity>('Files', user);
|
|
27
|
+
const fileCategoryEntity = await p.GetEntityObject<FileCategoryEntity>('File Categories', user);
|
|
29
28
|
|
|
30
29
|
fileEntity.CheckPermissions(EntityPermissionType.Update, true);
|
|
31
30
|
fileCategoryEntity.CheckPermissions(EntityPermissionType.Delete, true);
|
|
@@ -34,7 +33,7 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
34
33
|
const returnValue = fileCategoryEntity.GetAll();
|
|
35
34
|
|
|
36
35
|
// Any files using the deleted category fall back to its parent
|
|
37
|
-
await
|
|
36
|
+
await p.BeginTransaction();
|
|
38
37
|
try {
|
|
39
38
|
// SHOULD USE BaseEntity for each of these records to ensure object model
|
|
40
39
|
// is used everywhere - new code below. The below is SLOWER than a single
|
|
@@ -60,20 +59,20 @@ export class FileResolver extends FileCategoryResolverBase {
|
|
|
60
59
|
// iterate through each of the files in filesResult.Results
|
|
61
60
|
// and update the CategoryID to fileCategoryEntity.ParentID
|
|
62
61
|
for (const file of filesResult.Results) {
|
|
63
|
-
const fileEntity = await
|
|
62
|
+
const fileEntity = await p.GetEntityObject<FileEntity>('Files', user);
|
|
64
63
|
await fileEntity.Load(file.ID);
|
|
65
64
|
fileEntity.CategoryID = fileCategoryEntity.ParentID;
|
|
66
65
|
await fileEntity.Save();
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
await fileCategoryEntity.Delete(options);
|
|
70
|
-
await
|
|
69
|
+
await p.CommitTransaction();
|
|
71
70
|
} catch (error) {
|
|
72
|
-
await
|
|
71
|
+
await p.RollbackTransaction();
|
|
73
72
|
throw error;
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
await this.AfterDelete(
|
|
75
|
+
await this.AfterDelete(p, key); // fire event
|
|
77
76
|
return returnValue;
|
|
78
77
|
}
|
|
79
78
|
}
|
|
@@ -51,14 +51,13 @@ export class FileResolver extends FileResolverBase {
|
|
|
51
51
|
@Ctx() context: AppContext,
|
|
52
52
|
@PubSub() pubSub: PubSubEngine
|
|
53
53
|
) {
|
|
54
|
-
|
|
54
|
+
// Check to see if there's already an object with that name
|
|
55
|
+
const provider = GetReadOnlyProvider(context.providers, {allowFallbackToReadWrite: true})
|
|
55
56
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
56
|
-
const fileEntity = await
|
|
57
|
-
const providerEntity = await
|
|
57
|
+
const fileEntity = await provider.GetEntityObject<FileEntity>('Files', user);
|
|
58
|
+
const providerEntity = await provider.GetEntityObject<FileStorageProviderEntity>('File Storage Providers', user);
|
|
58
59
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
59
60
|
|
|
60
|
-
// Check to see if there's already an object with that name
|
|
61
|
-
const provider = GetReadOnlyProvider(context.providers, {allowFallbackToReadWrite: true})
|
|
62
61
|
const [sameName] = await this.findBy(
|
|
63
62
|
provider,
|
|
64
63
|
'Files',
|
|
@@ -108,7 +107,7 @@ export class FileResolver extends FileResolverBase {
|
|
|
108
107
|
@PubSub() pubSub: PubSubEngine
|
|
109
108
|
) {
|
|
110
109
|
// if the name is changing, rename the target object as well
|
|
111
|
-
const md =
|
|
110
|
+
const md = GetReadOnlyProvider(context.providers);
|
|
112
111
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
113
112
|
const fileEntity = await md.GetEntityObject<FileEntity>('Files', user);
|
|
114
113
|
fileEntity.CheckPermissions(EntityPermissionType.Update, true);
|
|
@@ -136,7 +135,7 @@ export class FileResolver extends FileResolverBase {
|
|
|
136
135
|
@Ctx() context: AppContext,
|
|
137
136
|
@PubSub() pubSub: PubSubEngine
|
|
138
137
|
) {
|
|
139
|
-
const md =
|
|
138
|
+
const md = GetReadOnlyProvider(context.providers);
|
|
140
139
|
const userInfo = this.GetUserFromPayload(context.userPayload);
|
|
141
140
|
|
|
142
141
|
const fileEntity = await md.GetEntityObject<FileEntity>('Files', userInfo);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, ObjectType, Query } from "type-graphql";
|
|
2
2
|
import { AppContext } from "../types.js";
|
|
3
3
|
import { DataContext } from "@memberjunction/data-context";
|
|
4
|
-
import { GetReadOnlyDataSource } from "../util.js";
|
|
4
|
+
import { GetReadOnlyDataSource, GetReadOnlyProvider } from "../util.js";
|
|
5
5
|
import { Metadata } from "@memberjunction/core";
|
|
6
6
|
import { DataContextItemEntity } from "@memberjunction/core-entities";
|
|
7
7
|
|
|
@@ -53,7 +53,7 @@ export class GetDataContextDataResolver {
|
|
|
53
53
|
const ds = GetReadOnlyDataSource(appCtx.dataSources, {
|
|
54
54
|
allowFallbackToReadWrite: true,
|
|
55
55
|
})
|
|
56
|
-
const md =
|
|
56
|
+
const md = GetReadOnlyProvider(appCtx.providers, {allowFallbackToReadWrite: true});
|
|
57
57
|
const dciData = await md.GetEntityObject<DataContextItemEntity>("Data Context Items", appCtx.userPayload.userRecord);
|
|
58
58
|
if (await dciData.Load(DataContextItemID)) {
|
|
59
59
|
const dci = DataContext.CreateDataContextItem(); // use class factory to get whatever lowest level sub-class is registered
|
|
@@ -3,7 +3,7 @@ import { AppContext } from '../types.js';
|
|
|
3
3
|
import { LogError, LogStatus, Metadata } from '@memberjunction/core';
|
|
4
4
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
5
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
-
import { GetReadOnlyDataSource } from '../util.js';
|
|
6
|
+
import { GetReadOnlyDataSource, GetReadOnlyProvider } from '../util.js';
|
|
7
7
|
import sql from 'mssql';
|
|
8
8
|
|
|
9
9
|
@InputType()
|
|
@@ -169,7 +169,7 @@ export class GetDataResolver {
|
|
|
169
169
|
@Ctx() context: AppContext
|
|
170
170
|
): Promise<SimpleEntityResultType> {
|
|
171
171
|
try {
|
|
172
|
-
const md =
|
|
172
|
+
const md = GetReadOnlyProvider(context.providers);
|
|
173
173
|
const result = md.Entities.map((e) => {
|
|
174
174
|
return {
|
|
175
175
|
ID: e.ID,
|