@memberjunction/server 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/auth/BaseAuthProvider.d.ts +1 -0
- package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
- package/dist/auth/BaseAuthProvider.js +2 -0
- package/dist/auth/BaseAuthProvider.js.map +1 -1
- package/dist/auth/IAuthProvider.d.ts +1 -0
- package/dist/auth/IAuthProvider.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/generated/generated.d.ts +431 -2
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +3052 -379
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +1 -0
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +30 -0
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/resolvers/APIKeyResolver.d.ts +2 -1
- package/dist/resolvers/APIKeyResolver.d.ts.map +1 -1
- package/dist/resolvers/APIKeyResolver.js +4 -1
- package/dist/resolvers/APIKeyResolver.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +2 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +4 -1
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +5 -4
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +7 -4
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts +2 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +3 -1
- package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +2 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +10 -3
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts +37 -0
- package/dist/resolvers/MCPResolver.d.ts.map +1 -0
- package/dist/resolvers/MCPResolver.js +363 -0
- package/dist/resolvers/MCPResolver.js.map +1 -0
- package/dist/resolvers/MergeRecordsResolver.d.ts +2 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +3 -1
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +2 -1
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +6 -1
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/ReportResolver.d.ts +2 -1
- package/dist/resolvers/ReportResolver.d.ts.map +1 -1
- package/dist/resolvers/ReportResolver.js +4 -1
- package/dist/resolvers/ReportResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +2 -0
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +3 -0
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +1 -0
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/TaskResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskResolver.js +1 -0
- package/dist/resolvers/TaskResolver.js.map +1 -1
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +4 -0
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/package.json +47 -46
- package/src/auth/BaseAuthProvider.ts +3 -0
- package/src/auth/IAuthProvider.ts +5 -0
- package/src/config.ts +2 -2
- package/src/generated/generated.ts +2020 -334
- package/src/generic/ResolverBase.ts +89 -3
- package/src/index.ts +10 -2
- package/src/resolvers/APIKeyResolver.ts +8 -1
- package/src/resolvers/ActionResolver.ts +8 -1
- package/src/resolvers/DatasetResolver.ts +11 -4
- package/src/resolvers/EntityCommunicationsResolver.ts +5 -1
- package/src/resolvers/GetDataContextDataResolver.ts +14 -6
- package/src/resolvers/MCPResolver.ts +480 -0
- package/src/resolvers/MergeRecordsResolver.ts +5 -1
- package/src/resolvers/QueryResolver.ts +17 -3
- package/src/resolvers/ReportResolver.ts +8 -1
- package/src/resolvers/RunAIAgentResolver.ts +6 -0
- package/src/resolvers/RunAIPromptResolver.ts +10 -1
- package/src/resolvers/RunTemplateResolver.ts +4 -1
- package/src/resolvers/TaskResolver.ts +3 -0
- package/src/resolvers/UserResolver.ts +15 -3
- package/dist/resolvers/AskSkipResolver.d.ts +0 -123
- package/dist/resolvers/AskSkipResolver.d.ts.map +0 -1
- package/dist/resolvers/AskSkipResolver.js +0 -1788
- package/dist/resolvers/AskSkipResolver.js.map +0 -1
- package/dist/scheduler/LearningCycleScheduler.d.ts +0 -4
- package/dist/scheduler/LearningCycleScheduler.d.ts.map +0 -1
- package/dist/scheduler/LearningCycleScheduler.js +0 -4
- package/dist/scheduler/LearningCycleScheduler.js.map +0 -1
- package/src/resolvers/AskSkipResolver.ts +0 -3446
- package/src/scheduler/LearningCycleScheduler.ts +0 -320
|
@@ -19,8 +19,9 @@ import {
|
|
|
19
19
|
} from '@memberjunction/core';
|
|
20
20
|
import { AuditLogEntity, ErrorLogEntity, UserViewEntityExtended } from '@memberjunction/core-entities';
|
|
21
21
|
import { SQLServerDataProvider, UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
22
|
-
import { PubSubEngine } from 'type-graphql';
|
|
22
|
+
import { PubSubEngine, AuthorizationError } from 'type-graphql';
|
|
23
23
|
import { GraphQLError } from 'graphql';
|
|
24
|
+
import { GetAPIKeyEngine } from '@memberjunction/api-keys';
|
|
24
25
|
import sql from 'mssql';
|
|
25
26
|
import { httpTransport, CloudEvent, emitterFor } from 'cloudevents';
|
|
26
27
|
|
|
@@ -504,7 +505,7 @@ export class ResolverBase {
|
|
|
504
505
|
if (!userPayload) {
|
|
505
506
|
throw new Error(`userPayload is null`);
|
|
506
507
|
}
|
|
507
|
-
|
|
508
|
+
|
|
508
509
|
// first check permissions, the logged in user must have read permissions on the entity to run the view
|
|
509
510
|
if (entityInfo) {
|
|
510
511
|
const userInfo = UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload.email.toLowerCase().trim()); // get the user record from MD so we have ROLES attached, don't use the one from payload directly
|
|
@@ -516,12 +517,85 @@ export class ResolverBase {
|
|
|
516
517
|
if (!userPermissions.CanRead) {
|
|
517
518
|
throw new Error(`User ${userPayload.email} does not have read permissions on ${entityInfo.Name}`);
|
|
518
519
|
}
|
|
519
|
-
}
|
|
520
|
+
}
|
|
520
521
|
else {
|
|
521
522
|
throw new Error(`Entity not found in metadata`);
|
|
522
523
|
}
|
|
523
524
|
}
|
|
524
525
|
|
|
526
|
+
/**
|
|
527
|
+
* Checks API key scope authorization. Only performs check if request
|
|
528
|
+
* was authenticated via API key (apiKeyHash present in userPayload).
|
|
529
|
+
* For OAuth/JWT auth, this is a no-op.
|
|
530
|
+
*
|
|
531
|
+
* @param scopePath - The scope path (e.g., 'entity:read', 'agent:execute')
|
|
532
|
+
* @param resource - The resource name (e.g., entity name, agent name)
|
|
533
|
+
* @param userPayload - The user payload from context
|
|
534
|
+
* @throws AuthorizationError if API key lacks required scope
|
|
535
|
+
*/
|
|
536
|
+
protected async CheckAPIKeyScopeAuthorization(
|
|
537
|
+
scopePath: string,
|
|
538
|
+
resource: string,
|
|
539
|
+
userPayload: UserPayload
|
|
540
|
+
): Promise<void> {
|
|
541
|
+
// Skip scope check for OAuth/JWT auth (no API key)
|
|
542
|
+
if (!userPayload.apiKeyHash) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Get system user for authorization call
|
|
547
|
+
// NOTE: We use system user here because Authorize() needs to run internal
|
|
548
|
+
// database queries (loading scope rules, logging decisions). The system user
|
|
549
|
+
// ensures these queries work regardless of what permissions the API key's
|
|
550
|
+
// user has. The API key's associated user (in userPayload.userRecord) is
|
|
551
|
+
// used later when the actual operation executes - their permissions are
|
|
552
|
+
// the ultimate ceiling that scopes can only narrow, never expand.
|
|
553
|
+
const systemUser = UserCache.Instance.Users.find(u => u.Type === 'System');
|
|
554
|
+
if (!systemUser) {
|
|
555
|
+
throw new Error('System user not found');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const apiKeyEngine = GetAPIKeyEngine();
|
|
559
|
+
|
|
560
|
+
// Check for full_access scope first (god power - bypasses all other checks)
|
|
561
|
+
const fullAccessResult = await apiKeyEngine.Authorize(
|
|
562
|
+
userPayload.apiKeyHash,
|
|
563
|
+
'MJAPI',
|
|
564
|
+
'full_access',
|
|
565
|
+
'*',
|
|
566
|
+
systemUser,
|
|
567
|
+
{ endpoint: '/graphql', method: 'POST' }
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
if (fullAccessResult.Allowed) {
|
|
571
|
+
// full_access granted - skip specific scope check
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Check specific scope
|
|
576
|
+
const result = await apiKeyEngine.Authorize(
|
|
577
|
+
userPayload.apiKeyHash,
|
|
578
|
+
'MJAPI',
|
|
579
|
+
scopePath,
|
|
580
|
+
resource,
|
|
581
|
+
systemUser,
|
|
582
|
+
{
|
|
583
|
+
endpoint: '/graphql',
|
|
584
|
+
method: 'POST'
|
|
585
|
+
}
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
if (!result.Allowed) {
|
|
589
|
+
// Provide specific, actionable error message
|
|
590
|
+
throw new AuthorizationError(
|
|
591
|
+
`Access denied. This API key requires the '${scopePath}' scope ` +
|
|
592
|
+
`for resource '${resource}' to perform this operation. ` +
|
|
593
|
+
`Please update the API key's scopes or use an API key with appropriate permissions. ` +
|
|
594
|
+
`Denial reason: ${result.Reason}`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
525
599
|
/**
|
|
526
600
|
* Optimized RunViewGenericInternal implementation with:
|
|
527
601
|
* - Field filtering at source (Fix #7)
|
|
@@ -550,6 +624,9 @@ export class ResolverBase {
|
|
|
550
624
|
try {
|
|
551
625
|
if (!viewInfo || !userPayload) return null;
|
|
552
626
|
|
|
627
|
+
// Check API key scope authorization for view operations
|
|
628
|
+
await this.CheckAPIKeyScopeAuthorization('view:run', viewInfo.Entity, userPayload);
|
|
629
|
+
|
|
553
630
|
const md = provider
|
|
554
631
|
const user = UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload?.email.toLowerCase().trim());
|
|
555
632
|
if (!user) throw new Error(`User ${userPayload?.email} not found in metadata`);
|
|
@@ -908,6 +985,9 @@ export class ResolverBase {
|
|
|
908
985
|
}
|
|
909
986
|
|
|
910
987
|
protected async CreateRecord(entityName: string, input: any, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
988
|
+
// Check API key scope authorization for entity create operations
|
|
989
|
+
await this.CheckAPIKeyScopeAuthorization('entity:create', entityName, userPayload);
|
|
990
|
+
|
|
911
991
|
if (await this.BeforeCreate(provider, input)) {
|
|
912
992
|
// fire event and proceed if it wasn't cancelled
|
|
913
993
|
const entityObject = await provider.GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
|
|
@@ -940,6 +1020,9 @@ export class ResolverBase {
|
|
|
940
1020
|
protected async AfterCreate(provider: DatabaseProviderBase, input: any) {}
|
|
941
1021
|
|
|
942
1022
|
protected async UpdateRecord(entityName: string, input: any, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
|
|
1023
|
+
// Check API key scope authorization for entity update operations
|
|
1024
|
+
await this.CheckAPIKeyScopeAuthorization('entity:update', entityName, userPayload);
|
|
1025
|
+
|
|
943
1026
|
if (await this.BeforeUpdate(provider, input)) {
|
|
944
1027
|
// fire event and proceed if it wasn't cancelled
|
|
945
1028
|
const userInfo = this.GetUserFromPayload(userPayload);
|
|
@@ -1202,6 +1285,9 @@ export class ResolverBase {
|
|
|
1202
1285
|
userPayload: UserPayload,
|
|
1203
1286
|
pubSub: PubSubEngine
|
|
1204
1287
|
) {
|
|
1288
|
+
// Check API key scope authorization for entity delete operations
|
|
1289
|
+
await this.CheckAPIKeyScopeAuthorization('entity:delete', entityName, userPayload);
|
|
1290
|
+
|
|
1205
1291
|
if (await this.BeforeDelete(provider, key)) {
|
|
1206
1292
|
// fire event and proceed if it wasn't cancelled
|
|
1207
1293
|
const entityObject = await provider.GetEntityObject(entityName, this.GetUserFromPayload(userPayload));
|
package/src/index.ts
CHANGED
|
@@ -81,7 +81,15 @@ export { configInfo, DEFAULT_SERVER_CONFIG } from './config.js';
|
|
|
81
81
|
export * from './directives/index.js';
|
|
82
82
|
export * from './entitySubclasses/entityPermissions.server.js';
|
|
83
83
|
export * from './types.js';
|
|
84
|
-
export {
|
|
84
|
+
export {
|
|
85
|
+
TokenExpiredError,
|
|
86
|
+
getSystemUser,
|
|
87
|
+
getSigningKeys,
|
|
88
|
+
extractUserInfoFromPayload,
|
|
89
|
+
verifyUserRecord,
|
|
90
|
+
AuthProviderFactory,
|
|
91
|
+
IAuthProvider,
|
|
92
|
+
} from './auth/index.js';
|
|
85
93
|
export * from './auth/APIKeyScopeAuth.js';
|
|
86
94
|
|
|
87
95
|
export * from './generic/PushStatusResolver.js';
|
|
@@ -98,7 +106,6 @@ export * from './generic/DeleteOptionsInput.js';
|
|
|
98
106
|
export * from './agents/skip-agent.js';
|
|
99
107
|
export * from './agents/skip-sdk.js';
|
|
100
108
|
|
|
101
|
-
export * from './resolvers/AskSkipResolver.js';
|
|
102
109
|
export * from './resolvers/ColorResolver.js';
|
|
103
110
|
export * from './resolvers/ComponentRegistryResolver.js';
|
|
104
111
|
export * from './resolvers/DatasetResolver.js';
|
|
@@ -115,6 +122,7 @@ export * from './resolvers/TransactionGroupResolver.js';
|
|
|
115
122
|
export * from './resolvers/CreateQueryResolver.js';
|
|
116
123
|
export * from './resolvers/TelemetryResolver.js';
|
|
117
124
|
export * from './resolvers/APIKeyResolver.js';
|
|
125
|
+
export * from './resolvers/MCPResolver.js';
|
|
118
126
|
export { GetReadOnlyDataSource, GetReadWriteDataSource, GetReadWriteProvider, GetReadOnlyProvider } from './util.js';
|
|
119
127
|
|
|
120
128
|
export * from './generated/generated.js';
|
|
@@ -4,6 +4,7 @@ import { LogError, Metadata } from "@memberjunction/core";
|
|
|
4
4
|
import { APIKeyScopeEntity } from "@memberjunction/core-entities";
|
|
5
5
|
import { GetAPIKeyEngine } from "@memberjunction/api-keys";
|
|
6
6
|
import { AppContext } from "../types.js";
|
|
7
|
+
import { ResolverBase } from "../generic/ResolverBase.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Input type for creating a new API key
|
|
@@ -90,7 +91,7 @@ export class RevokeAPIKeyResult {
|
|
|
90
91
|
* Handles secure server-side API key generation
|
|
91
92
|
*/
|
|
92
93
|
@Resolver()
|
|
93
|
-
export class APIKeyResolver {
|
|
94
|
+
export class APIKeyResolver extends ResolverBase {
|
|
94
95
|
/**
|
|
95
96
|
* Creates a new API key with proper server-side cryptographic hashing.
|
|
96
97
|
*
|
|
@@ -109,6 +110,9 @@ export class APIKeyResolver {
|
|
|
109
110
|
@Arg("input") input: CreateAPIKeyInput,
|
|
110
111
|
@Ctx() ctx: AppContext
|
|
111
112
|
): Promise<CreateAPIKeyResult> {
|
|
113
|
+
// Check API key scope authorization for API key creation
|
|
114
|
+
await this.CheckAPIKeyScopeAuthorization('apikey:create', '*', ctx.userPayload);
|
|
115
|
+
|
|
112
116
|
try {
|
|
113
117
|
// Get the authenticated user
|
|
114
118
|
const user = ctx.userPayload.userRecord;
|
|
@@ -173,6 +177,9 @@ export class APIKeyResolver {
|
|
|
173
177
|
@Arg("apiKeyId") apiKeyId: string,
|
|
174
178
|
@Ctx() ctx: AppContext
|
|
175
179
|
): Promise<RevokeAPIKeyResult> {
|
|
180
|
+
// Check API key scope authorization for API key revocation
|
|
181
|
+
await this.CheckAPIKeyScopeAuthorization('apikey:revoke', apiKeyId, ctx.userPayload);
|
|
182
|
+
|
|
176
183
|
try {
|
|
177
184
|
const user = ctx.userPayload.userRecord;
|
|
178
185
|
if (!user) {
|
|
@@ -8,6 +8,7 @@ import { KeyValuePairInput } from "../generic/KeyValuePairInput.js";
|
|
|
8
8
|
import { AppContext, ProviderInfo } from "../types.js";
|
|
9
9
|
import { CopyScalarsAndArrays } from "@memberjunction/global";
|
|
10
10
|
import { GetReadOnlyProvider } from "../util.js";
|
|
11
|
+
import { ResolverBase } from "../generic/ResolverBase.js";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Input type for action parameters
|
|
@@ -171,7 +172,7 @@ export class ActionResultOutput {
|
|
|
171
172
|
* Handles running actions and entity actions through GraphQL
|
|
172
173
|
*/
|
|
173
174
|
@Resolver()
|
|
174
|
-
export class ActionResolver {
|
|
175
|
+
export class ActionResolver extends ResolverBase {
|
|
175
176
|
/**
|
|
176
177
|
* Mutation for running an action
|
|
177
178
|
* @param input The input parameters for running the action
|
|
@@ -184,6 +185,9 @@ export class ActionResolver {
|
|
|
184
185
|
@Ctx() ctx: AppContext
|
|
185
186
|
): Promise<ActionResultOutput> {
|
|
186
187
|
try {
|
|
188
|
+
// Check API key scope authorization for action execution
|
|
189
|
+
await this.CheckAPIKeyScopeAuthorization('action:execute', input.ActionID, ctx.userPayload);
|
|
190
|
+
|
|
187
191
|
// Get the user from context
|
|
188
192
|
const user = ctx.userPayload.userRecord;
|
|
189
193
|
if (!user) {
|
|
@@ -326,6 +330,9 @@ export class ActionResolver {
|
|
|
326
330
|
@Ctx() ctx: AppContext
|
|
327
331
|
): Promise<ActionResultOutput> {
|
|
328
332
|
try {
|
|
333
|
+
// Check API key scope authorization for entity action execution
|
|
334
|
+
await this.CheckAPIKeyScopeAuthorization('action:execute', input.EntityActionID, ctx.userPayload);
|
|
335
|
+
|
|
329
336
|
const user = ctx.userPayload.userRecord;
|
|
330
337
|
if (!user) {
|
|
331
338
|
throw new Error("User is not authenticated");
|
|
@@ -2,6 +2,7 @@ import { Arg, Ctx, Field, InputType, Int, ObjectType, Query, Resolver } from 'ty
|
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
3
|
import { LogError, Metadata } from '@memberjunction/core';
|
|
4
4
|
import { GetReadOnlyProvider } from '../util.js';
|
|
5
|
+
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
5
6
|
|
|
6
7
|
@ObjectType()
|
|
7
8
|
export class DatasetResultType {
|
|
@@ -35,13 +36,16 @@ export class DatasetItemFilterTypeGQL {
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
@Resolver(DatasetResultType)
|
|
38
|
-
export class DatasetResolverExtended {
|
|
39
|
+
export class DatasetResolverExtended extends ResolverBase {
|
|
39
40
|
@Query(() => DatasetResultType)
|
|
40
41
|
async GetDatasetByName(
|
|
41
42
|
@Arg('DatasetName', () => String) DatasetName: string,
|
|
42
|
-
@Ctx() {providers}: AppContext,
|
|
43
|
+
@Ctx() { providers, userPayload }: AppContext,
|
|
43
44
|
@Arg('ItemFilters', () => [DatasetItemFilterTypeGQL], { nullable: 'itemsAndList' }) ItemFilters?: DatasetItemFilterTypeGQL[]
|
|
44
45
|
) {
|
|
46
|
+
// Check API key scope authorization for dataset read
|
|
47
|
+
await this.CheckAPIKeyScopeAuthorization('dataset:read', DatasetName, userPayload);
|
|
48
|
+
|
|
45
49
|
try {
|
|
46
50
|
const md = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
47
51
|
const result = await md.GetDatasetByName(DatasetName, ItemFilters);
|
|
@@ -86,13 +90,16 @@ export class DatasetStatusResultType {
|
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
@Resolver(DatasetStatusResultType)
|
|
89
|
-
export class DatasetStatusResolver {
|
|
93
|
+
export class DatasetStatusResolver extends ResolverBase {
|
|
90
94
|
@Query(() => DatasetStatusResultType)
|
|
91
95
|
async GetDatasetStatusByName(
|
|
92
96
|
@Arg('DatasetName', () => String) DatasetName: string,
|
|
93
|
-
@Ctx() {providers}: AppContext,
|
|
97
|
+
@Ctx() { providers, userPayload }: AppContext,
|
|
94
98
|
@Arg('ItemFilters', () => [DatasetItemFilterTypeGQL], { nullable: 'itemsAndList' }) ItemFilters?: DatasetItemFilterTypeGQL[]
|
|
95
99
|
) {
|
|
100
|
+
// Check API key scope authorization for dataset read
|
|
101
|
+
await this.CheckAPIKeyScopeAuthorization('dataset:read', DatasetName, userPayload);
|
|
102
|
+
|
|
96
103
|
try {
|
|
97
104
|
const md = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true});
|
|
98
105
|
const result = await md.GetDatasetStatusByName(DatasetName, ItemFilters);
|
|
@@ -8,6 +8,7 @@ import { GraphQLJSONObject } from 'graphql-type-json';
|
|
|
8
8
|
import { TemplateEngineServer } from '@memberjunction/templates';
|
|
9
9
|
import { EntityCommunicationParams } from '@memberjunction/entity-communications-base';
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
+
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
11
12
|
|
|
12
13
|
@InputType()
|
|
13
14
|
export class CommunicationProviderMessageType {
|
|
@@ -166,7 +167,7 @@ export class RunEntityCommunicationResultType {
|
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
@Resolver(RunEntityCommunicationResultType)
|
|
169
|
-
export class ReportResolver {
|
|
170
|
+
export class ReportResolver extends ResolverBase {
|
|
170
171
|
@Query(() => RunEntityCommunicationResultType)
|
|
171
172
|
async RunEntityCommunicationByViewID(
|
|
172
173
|
@Arg('entityID', () => String) entityID: string,
|
|
@@ -178,6 +179,9 @@ export class ReportResolver {
|
|
|
178
179
|
@Arg('includeProcessedMessages', () => Boolean) includeProcessedMessages: boolean,
|
|
179
180
|
@Ctx() { userPayload }: AppContext
|
|
180
181
|
): Promise<RunEntityCommunicationResultType> {
|
|
182
|
+
// Check API key scope authorization for communication send
|
|
183
|
+
await this.CheckAPIKeyScopeAuthorization('communication:send', entityID, userPayload);
|
|
184
|
+
|
|
181
185
|
try {
|
|
182
186
|
await EntityCommunicationsEngine.Instance.Config(false, userPayload.userRecord);
|
|
183
187
|
const newMessage = new Message(message as unknown as Message);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Arg, Ctx, Field, ObjectType, Query } from "type-graphql";
|
|
1
|
+
import { Arg, Ctx, Field, ObjectType, Query, Resolver } from "type-graphql";
|
|
2
2
|
import { AppContext } from "../types.js";
|
|
3
3
|
import { DataContext } from "@memberjunction/data-context";
|
|
4
4
|
import { GetReadOnlyDataSource, GetReadOnlyProvider } from "../util.js";
|
|
5
5
|
import { Metadata } from "@memberjunction/core";
|
|
6
6
|
import { DataContextItemEntity } from "@memberjunction/core-entities";
|
|
7
|
+
import { ResolverBase } from "../generic/ResolverBase.js";
|
|
7
8
|
|
|
8
9
|
@ObjectType()
|
|
9
10
|
export class GetDataContextItemDataOutputType {
|
|
@@ -39,16 +40,20 @@ export class GetDataContextDataOutputType {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
@Resolver()
|
|
44
|
+
export class GetDataContextDataResolver extends ResolverBase {
|
|
43
45
|
/**
|
|
44
|
-
* Returns data for a given data context item.
|
|
45
|
-
* @param DataContextItemID
|
|
46
|
+
* Returns data for a given data context item.
|
|
47
|
+
* @param DataContextItemID
|
|
46
48
|
*/
|
|
47
49
|
@Query(() => GetDataContextItemDataOutputType)
|
|
48
50
|
async GetDataContextItemData(
|
|
49
51
|
@Arg('DataContextItemID', () => String) DataContextItemID: string,
|
|
50
52
|
@Ctx() appCtx: AppContext
|
|
51
53
|
) {
|
|
54
|
+
// Check API key scope authorization for data context read
|
|
55
|
+
await this.CheckAPIKeyScopeAuthorization('datacontext:read', DataContextItemID, appCtx.userPayload);
|
|
56
|
+
|
|
52
57
|
try {
|
|
53
58
|
const ds = GetReadOnlyDataSource(appCtx.dataSources, {
|
|
54
59
|
allowFallbackToReadWrite: true,
|
|
@@ -92,14 +97,17 @@ export class GetDataContextDataResolver {
|
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
/**
|
|
95
|
-
* Returns data for a given data context.
|
|
96
|
-
* @param DataContextID
|
|
100
|
+
* Returns data for a given data context.
|
|
101
|
+
* @param DataContextID
|
|
97
102
|
*/
|
|
98
103
|
@Query(() => GetDataContextDataOutputType)
|
|
99
104
|
async GetDataContextData(
|
|
100
105
|
@Arg('DataContextID', () => String) DataContextID: string,
|
|
101
106
|
@Ctx() appCtx: AppContext
|
|
102
107
|
) {
|
|
108
|
+
// Check API key scope authorization for data context read
|
|
109
|
+
await this.CheckAPIKeyScopeAuthorization('datacontext:read', DataContextID, appCtx.userPayload);
|
|
110
|
+
|
|
103
111
|
try {
|
|
104
112
|
// our job here is to load the entire data context, so we do that with the Data Context object
|
|
105
113
|
const dc = new DataContext();
|